From 148b1f1708913490752de798ca640b427e471ea2 Mon Sep 17 00:00:00 2001 From: Winston-Yieldmo <46379634+Winston-Yieldmo@users.noreply.github.com> Date: Wed, 15 Jan 2020 15:12:30 -0500 Subject: [PATCH 001/318] adding yieldmo vendor id to usersync (#1166) --- adapters/yieldmo/usersync.go | 2 +- adapters/yieldmo/usersync_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/yieldmo/usersync.go b/adapters/yieldmo/usersync.go index f853bbb86a5..041e7e8f073 100644 --- a/adapters/yieldmo/usersync.go +++ b/adapters/yieldmo/usersync.go @@ -8,5 +8,5 @@ import ( ) func NewYieldmoSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("yieldmo", 0, temp, adapters.SyncTypeRedirect) + return adapters.NewSyncer("yieldmo", 173, temp, adapters.SyncTypeRedirect) } diff --git a/adapters/yieldmo/usersync_test.go b/adapters/yieldmo/usersync_test.go index 5ae437c8f00..598710ec742 100644 --- a/adapters/yieldmo/usersync_test.go +++ b/adapters/yieldmo/usersync_test.go @@ -25,6 +25,6 @@ func TestYieldmoSyncer(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "//ads.yieldmo.com/pbsync?gdpr=0&gdpr_consent=&us_privacy=&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL) assert.Equal(t, "redirect", syncInfo.Type) - assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.EqualValues(t, 173, syncer.GDPRVendorID()) assert.False(t, syncInfo.SupportCORS) } From 7d9b1ece872c8fb1a339ebba99f0878ac121de16 Mon Sep 17 00:00:00 2001 From: evanmsmrtb Date: Thu, 16 Jan 2020 10:13:54 -0600 Subject: [PATCH 002/318] Add SmartRTB adapter (#1071) --- adapters/smartrtb/smartrtb.go | 189 ++++++++++++++++++ adapters/smartrtb/smartrtb_test.go | 11 + .../smartrtbtest/exemplary/banner.json | 134 +++++++++++++ .../smartrtbtest/exemplary/video.json | 134 +++++++++++++ .../smartrtbtest/params/race/banner.json | 5 + .../smartrtbtest/params/race/video.json | 5 + .../supplemental/bad-bidder-ext.json | 31 +++ .../supplemental/bad-imp-ext.json | 32 +++ .../supplemental/bad-pub-value-empty.json | 37 ++++ .../supplemental/bad-pub-value.json | 37 ++++ .../supplemental/bad-request.json | 70 +++++++ .../smartrtbtest/supplemental/empty-imps.json | 14 ++ .../supplemental/invalid-bid-ext.json | 93 +++++++++ .../supplemental/invalid-bid-format.json | 95 +++++++++ .../supplemental/invalid-bid-json.json | 76 +++++++ .../supplemental/invalid-imp-ext.json | 32 +++ .../smartrtbtest/supplemental/nobid.json | 69 +++++++ .../supplemental/non-http-ok.json | 76 +++++++ adapters/smartrtb/usersync.go | 12 ++ adapters/smartrtb/usersync_test.go | 20 ++ config/config.go | 2 + docs/bidders/smartrtb.md | 39 ++++ exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_smartrtb.go | 8 + static/bidder-info/smartrtb.yaml | 11 + static/bidder-params/smartrtb.json | 27 +++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 29 files changed, 1266 insertions(+) create mode 100644 adapters/smartrtb/smartrtb.go create mode 100644 adapters/smartrtb/smartrtb_test.go create mode 100644 adapters/smartrtb/smartrtbtest/exemplary/banner.json create mode 100644 adapters/smartrtb/smartrtbtest/exemplary/video.json create mode 100644 adapters/smartrtb/smartrtbtest/params/race/banner.json create mode 100644 adapters/smartrtb/smartrtbtest/params/race/video.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/bad-bidder-ext.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/bad-imp-ext.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value-empty.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/bad-request.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/empty-imps.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-ext.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-format.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-json.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/invalid-imp-ext.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/nobid.json create mode 100644 adapters/smartrtb/smartrtbtest/supplemental/non-http-ok.json create mode 100644 adapters/smartrtb/usersync.go create mode 100644 adapters/smartrtb/usersync_test.go create mode 100644 docs/bidders/smartrtb.md create mode 100644 openrtb_ext/imp_smartrtb.go create mode 100644 static/bidder-info/smartrtb.yaml create mode 100644 static/bidder-params/smartrtb.json diff --git a/adapters/smartrtb/smartrtb.go b/adapters/smartrtb/smartrtb.go new file mode 100644 index 00000000000..5edaae6f289 --- /dev/null +++ b/adapters/smartrtb/smartrtb.go @@ -0,0 +1,189 @@ +package smartrtb + +import ( + "encoding/json" + "fmt" + "net/http" + "text/template" + + "github.com/golang/glog" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// Base adapter structure. +type SmartRTBAdapter struct { + EndpointTemplate template.Template +} + +// Bid request extension appended to downstream request. +// PubID are non-empty iff request.{App,Site} or +// request.{App,Site}.Publisher are nil, respectively. +type bidRequestExt struct { + PubID string `json:"pub_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + ForceBid bool `json:"force_bid,omitempty"` +} + +// bidExt.CreativeType values. +const ( + creativeTypeBanner string = "BANNER" + creativeTypeVideo = "VIDEO" + creativeTypeNative = "NATIVE" + creativeTypeAudio = "AUDIO" +) + +// Bid response extension from downstream. +type bidExt struct { + CreativeType string `json:"format"` +} + +func NewSmartRTBBidder(endpointTemplate string) adapters.Bidder { + template, err := template.New("endpointTemplate").Parse(endpointTemplate) + if err != nil { + glog.Fatal("Template URL error") + return nil + } + return &SmartRTBAdapter{EndpointTemplate: *template} +} + +func (adapter *SmartRTBAdapter) buildEndpointURL(pubID string) (string, error) { + endpointParams := macros.EndpointTemplateParams{PublisherID: pubID} + return macros.ResolveMacros(adapter.EndpointTemplate, endpointParams) +} + +func parseExtImp(dst *bidRequestExt, imp *openrtb.Imp) error { + var ext adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &ext); err != nil { + return adapters.BadInput(err.Error()) + } + + var src openrtb_ext.ExtImpSmartRTB + if err := json.Unmarshal(ext.Bidder, &src); err != nil { + return adapters.BadInput(err.Error()) + } + + if dst.PubID == "" { + dst.PubID = src.PubID + } + + if src.ZoneID != "" { + imp.TagID = src.ZoneID + } + return nil +} + +func (s *SmartRTBAdapter) MakeRequests(brq *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var imps []openrtb.Imp + var err error + ext := bidRequestExt{} + nrImps := len(brq.Imp) + errs := make([]error, 0, nrImps) + + for i := 0; i < nrImps; i++ { + imp := brq.Imp[i] + if imp.Banner == nil && imp.Video == nil { + continue + } + + err = parseExtImp(&ext, &imp) + if err != nil { + errs = append(errs, err) + continue + } + + imps = append(imps, imp) + } + + if len(imps) == 0 { + return nil, errs + } + + if ext.PubID == "" { + return nil, append(errs, adapters.BadInput("Cannot infer publisher ID from bid ext")) + } + + brq.Ext, err = json.Marshal(ext) + if err != nil { + return nil, append(errs, err) + } + + brq.Imp = imps + + rq, err := json.Marshal(brq) + if err != nil { + return nil, append(errs, err) + } + + url, err := s.buildEndpointURL(ext.PubID) + if err != nil { + return nil, append(errs, err) + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + return []*adapters.RequestData{{ + Method: "POST", + Uri: url, + Body: rq, + Headers: headers, + }}, errs +} + +func (s *SmartRTBAdapter) MakeBids( + brq *openrtb.BidRequest, drq *adapters.RequestData, + rs *adapters.ResponseData, +) (*adapters.BidderResponse, []error) { + if rs.StatusCode == http.StatusNoContent { + return nil, nil + } else if rs.StatusCode == http.StatusBadRequest { + return nil, []error{adapters.BadInput("Invalid request.")} + } else if rs.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected HTTP status %d.", rs.StatusCode), + }} + } + + var brs openrtb.BidResponse + if err := json.Unmarshal(rs.Body, &brs); err != nil { + return nil, []error{err} + } + + rv := adapters.NewBidderResponseWithBidsCapacity(5) + for _, seat := range brs.SeatBid { + for i := range seat.Bid { + var ext bidExt + if err := json.Unmarshal(seat.Bid[i].Ext, &ext); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Invalid bid extension from endpoint.", + }} + } + + var btype openrtb_ext.BidType + switch ext.CreativeType { + case creativeTypeBanner: + btype = openrtb_ext.BidTypeBanner + case creativeTypeVideo: + btype = openrtb_ext.BidTypeVideo + default: + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unsupported creative type %s.", + ext.CreativeType), + }} + } + + seat.Bid[i].Ext = nil + + rv.Bids = append(rv.Bids, &adapters.TypedBid{ + Bid: &seat.Bid[i], + BidType: btype, + }) + } + } + return rv, nil +} diff --git a/adapters/smartrtb/smartrtb_test.go b/adapters/smartrtb/smartrtb_test.go new file mode 100644 index 00000000000..3f76ed044a8 --- /dev/null +++ b/adapters/smartrtb/smartrtb_test.go @@ -0,0 +1,11 @@ +package smartrtb + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "smartrtbtest", NewSmartRTBBidder("http://market-east.smrtb.com/json/publisher/rtb?pubid=test")) +} diff --git a/adapters/smartrtb/smartrtbtest/exemplary/banner.json b/adapters/smartrtb/smartrtbtest/exemplary/banner.json new file mode 100644 index 00000000000..436f6298a16 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/exemplary/banner.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXK", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "abc", + "seatbid": [ + { + "bid": [ + { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "1", + "price": 0.01, + "ext": { + "format": "BANNER" + } + } + ] + }, + { + "bid": [ + { + "adm": "", + "crid": "test_video_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "2", + "price": 0.01, + "ext": { + "format": "VIDEO" + } + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "price": 0.01, + "id": "1" + }, + "type": "banner" + }, + { + "bid": { + "adm": "", + "crid": "test_video_crid", + "cid": "test_cid", + "impid": "imp123", + "price": 0.01, + "id": "2" + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/exemplary/video.json b/adapters/smartrtb/smartrtbtest/exemplary/video.json new file mode 100644 index 00000000000..436f6298a16 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/exemplary/video.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXK", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "abc", + "seatbid": [ + { + "bid": [ + { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "1", + "price": 0.01, + "ext": { + "format": "BANNER" + } + } + ] + }, + { + "bid": [ + { + "adm": "", + "crid": "test_video_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "2", + "price": 0.01, + "ext": { + "format": "VIDEO" + } + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "price": 0.01, + "id": "1" + }, + "type": "banner" + }, + { + "bid": { + "adm": "", + "crid": "test_video_crid", + "cid": "test_cid", + "impid": "imp123", + "price": 0.01, + "id": "2" + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/params/race/banner.json b/adapters/smartrtb/smartrtbtest/params/race/banner.json new file mode 100644 index 00000000000..207a504539f --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true +} \ No newline at end of file diff --git a/adapters/smartrtb/smartrtbtest/params/race/video.json b/adapters/smartrtb/smartrtbtest/params/race/video.json new file mode 100644 index 00000000000..207a504539f --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/params/race/video.json @@ -0,0 +1,5 @@ +{ + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true +} \ No newline at end of file diff --git a/adapters/smartrtb/smartrtbtest/supplemental/bad-bidder-ext.json b/adapters/smartrtb/smartrtbtest/supplemental/bad-bidder-ext.json new file mode 100644 index 00000000000..b261415de4d --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/bad-bidder-ext.json @@ -0,0 +1,31 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": null + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/bad-imp-ext.json b/adapters/smartrtb/smartrtbtest/supplemental/bad-imp-ext.json new file mode 100644 index 00000000000..1c0f57d2f34 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/bad-imp-ext.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "publisher": { }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": null + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value-empty.json b/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value-empty.json new file mode 100644 index 00000000000..aca21036b24 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value-empty.json @@ -0,0 +1,37 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "publisher": { }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Cannot infer publisher ID from bid ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value.json b/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value.json new file mode 100644 index 00000000000..93b45c747fd --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/bad-pub-value.json @@ -0,0 +1,37 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": 0, + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal number into Go struct field ExtImpSmartRTB.pub_id of type string", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/bad-request.json b/adapters/smartrtb/smartrtbtest/supplemental/bad-request.json new file mode 100644 index 00000000000..cf03832ddff --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/bad-request.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "abc", + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "fake", + "force_bid": false + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "imp": [{ + "id": "imp123", + "tagid": "fake", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "fake", + "force_bid": false + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 400 + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Invalid request.", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/empty-imps.json b/adapters/smartrtb/smartrtbtest/supplemental/empty-imps.json new file mode 100644 index 00000000000..a92add70825 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/empty-imps.json @@ -0,0 +1,14 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "publisher": { }, + "imp": [ + { + "id": "imp123" + } + ] + } +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-ext.json b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-ext.json new file mode 100644 index 00000000000..49527e1ecd4 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-ext.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXK", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "abc", + "seatbid": [ + { + "bid": [ + { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "1", + "price": 0.01, + "ext": "notvalidjsonhaha" + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Invalid bid extension from endpoint.", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-format.json b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-format.json new file mode 100644 index 00000000000..2f6bc07edb8 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-format.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXK", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "abc", + "seatbid": [ + { + "bid": [ + { + "adm": "hi", + "crid": "test_banner_crid", + "cid": "test_cid", + "impid": "imp123", + "id": "1", + "price": 0.01, + "ext": { + "format": "ALIEN_FORMAT" + } + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unsupported creative type ALIEN_FORMAT.", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-json.json b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-json.json new file mode 100644 index 00000000000..c56e7f21515 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/invalid-bid-json.json @@ -0,0 +1,76 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXK", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": "imnotyourfather" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/invalid-imp-ext.json b/adapters/smartrtb/smartrtbtest/supplemental/invalid-imp-ext.json new file mode 100644 index 00000000000..13485f797ba --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/invalid-imp-ext.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "publisher": { }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": "notjsontho" + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/nobid.json b/adapters/smartrtb/smartrtbtest/supplemental/nobid.json new file mode 100644 index 00000000000..2733d8dba96 --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/nobid.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXKz", + "force_bid": false + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXKz", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXKz", + "force_bid": false + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/smartrtb/smartrtbtest/supplemental/non-http-ok.json b/adapters/smartrtb/smartrtbtest/supplemental/non-http-ok.json new file mode 100644 index 00000000000..3acafadc62f --- /dev/null +++ b/adapters/smartrtb/smartrtbtest/supplemental/non-http-ok.json @@ -0,0 +1,76 @@ +{ + "mockBidRequest": { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [ + { + "id": "imp123", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXKz", + "force_bid": false + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://market-east.smrtb.com/json/publisher/rtb?pubid=test", + "body":{ + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "imp123", + "tagid": "N4zTDq3PPEHBIODv7cXKz", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXKz", + "force_bid": false + } + } + }], + "ext": { + "pub_id": "test" + } + } + }, + "mockResponse": { + "status": 500 + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected HTTP status 500.", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartrtb/usersync.go b/adapters/smartrtb/usersync.go new file mode 100644 index 00000000000..2f7b1dc3339 --- /dev/null +++ b/adapters/smartrtb/usersync.go @@ -0,0 +1,12 @@ +package smartrtb + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewSmartRTBSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("smartrtb", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/smartrtb/usersync_test.go b/adapters/smartrtb/usersync_test.go new file mode 100644 index 00000000000..ae3ae5dc007 --- /dev/null +++ b/adapters/smartrtb/usersync_test.go @@ -0,0 +1,20 @@ +package smartrtb + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/stretchr/testify/assert" +) + +func TestSmartRTBSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("http://market-east.smrtb.com/sync/all?nid=smartrtb&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&url=localhost%2Fsetuid%3Fbidder%smartrtb%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D")) + syncer := NewSmartRTBSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{}) + assert.NoError(t, err) + assert.Equal(t, "http://market-east.smrtb.com/sync/all?nid=smartrtb&gdpr=&gdpr_consent=&url=localhost%2Fsetuid%3Fbidder%smartrtb%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24%7BUID%7D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 722aae1395c..8dc0bb14526 100644 --- a/config/config.go +++ b/config/config.go @@ -518,6 +518,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderRTBHouse doesn't have a good default. // openrtb_ext.BidderRubicon doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSharethrough, "https://match.sharethrough.com/FGMrCMMc/v1?redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsharethrough%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSmartRTB, "https://market-global.smrtb.com/sync/all?nid=smartrtb&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&rr="+url.QueryEscape(externalURL)+"%252Fsetuid%253Fbidder%253Dsmartrtb%2526gdpr%253D{{.GDPR}}%2526gdpr_consent%253D{{.GDPRConsent}}%2526uid%253D%257BXID%257D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSomoaudience, "https://publisher-east.mobileadtrading.com/usersync?ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSonobi, "https://sync.go.sonobi.com/us.gif?loc="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D{{.GDPR}}%26gdpr%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -700,6 +701,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rtbhouse.endpoint", "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids") v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json") v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") + v.SetDefault("adapters.smartrtb.endpoint", "http://market-east.smrtb.com/json/publisher/rtb?pubid={{.PublisherID}}") v.SetDefault("adapters.somoaudience.endpoint", "http://publisher-east.mobileadtrading.com/rtb/bid") v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af") v.SetDefault("adapters.sovrn.endpoint", "http://ap.lijit.com/rtb/bid?src=prebid_server") diff --git a/docs/bidders/smartrtb.md b/docs/bidders/smartrtb.md new file mode 100644 index 00000000000..ffa88f663e8 --- /dev/null +++ b/docs/bidders/smartrtb.md @@ -0,0 +1,39 @@ +# SmartRTB Bidder + +[SmartRTB](https://smrtb.com/) supports the following parameters to be present in the `ext` object of impression requests: + +- "pub_id" type string - Required. Publisher ID assigned to you. +- "zone_id" type string - Optional. Enables mapping for further settings and reporting in the Marketplace UI. +- "force_bid" type bool - Optional. If zone ID is mapped, this may be set to always return fake sample bids (banner, video) + +Please contact us to create a new Smart RTB Marketplace account, and for any assistance in configuration. +You may email info@smrtb.com for inquiries. + +## Test Request + +This sample request is our global test placement and should always return a branded banner bid. + +``` + { + "id": "abc", + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "test", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "smartrtb": { + "pub_id": "test", + "zone_id": "N4zTDq3PPEHBIODv7cXK", + "force_bid": true + } + } + }] + } +``` diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 30a31727d6b..5795ad2c197 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -44,6 +44,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/smartrtb" "github.com/prebid/prebid-server/adapters/somoaudience" "github.com/prebid/prebid-server/adapters/sonobi" "github.com/prebid/prebid-server/adapters/sovrn" @@ -108,6 +109,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker), openrtb_ext.BidderSharethrough: sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint), + openrtb_ext.BidderSmartRTB: smartrtb.NewSmartRTBBidder(cfg.Adapters[string(openrtb_ext.BidderSmartRTB)].Endpoint), openrtb_ext.BidderSomoaudience: somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint), openrtb_ext.BidderSonobi: sonobi.NewSonobiBidder(client, cfg.Adapters[string(openrtb_ext.BidderSonobi)].Endpoint), openrtb_ext.BidderSovrn: sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 9621b23dc81..f02a16b4350 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -57,6 +57,7 @@ const ( BidderRTBHouse BidderName = "rtbhouse" BidderRubicon BidderName = "rubicon" BidderSharethrough BidderName = "sharethrough" + BidderSmartRTB BidderName = "smartrtb" BidderSomoaudience BidderName = "somoaudience" BidderSonobi BidderName = "sonobi" BidderSovrn BidderName = "sovrn" @@ -110,6 +111,7 @@ var BidderMap = map[string]BidderName{ "rtbhouse": BidderRTBHouse, "rubicon": BidderRubicon, "sharethrough": BidderSharethrough, + "smartrtb": BidderSmartRTB, "somoaudience": BidderSomoaudience, "sonobi": BidderSonobi, "sovrn": BidderSovrn, diff --git a/openrtb_ext/imp_smartrtb.go b/openrtb_ext/imp_smartrtb.go new file mode 100644 index 00000000000..d056046bf9d --- /dev/null +++ b/openrtb_ext/imp_smartrtb.go @@ -0,0 +1,8 @@ +package openrtb_ext + +type ExtImpSmartRTB struct { + PubID string `json:"pub_id,omitempty"` + MedID string `json:"med_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + ForceBid bool `json:"force_bid,omitempty"` +} diff --git a/static/bidder-info/smartrtb.yaml b/static/bidder-info/smartrtb.yaml new file mode 100644 index 00000000000..c26184f91b7 --- /dev/null +++ b/static/bidder-info/smartrtb.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "engineering@smrtb.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/smartrtb.json b/static/bidder-params/smartrtb.json new file mode 100644 index 00000000000..3bbaab10736 --- /dev/null +++ b/static/bidder-params/smartrtb.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "SmartRTB Adapter Params", + "description": "Required parameters for the SmartRTB server adapter", + "type": "object", + "properties": { + "pub_id": { + "type": "string", + "description": "Assigned publisher ID", + "minLength": 4 + }, + "med_id": { + "type": "string", + "description": "Property ID not zone ID not provided" + }, + "zone_id": { + "type": "string", + "description": "Specific zone ID for this placement, belonging to app/site", + "minLength": 20 + }, + "force_bid": { + "type": "boolean", + "description": "Force bids with a test creative" + } + }, + "required": [ "pub_id" ] + } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 6277993238a..2bfa5514063 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -40,6 +40,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/smartrtb" "github.com/prebid/prebid-server/adapters/somoaudience" "github.com/prebid/prebid-server/adapters/sonobi" "github.com/prebid/prebid-server/adapters/sovrn" @@ -99,6 +100,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderSomoaudience, somoaudience.NewSomoaudienceSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSonobi, sonobi.NewSonobiSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderSmartRTB, smartrtb.NewSmartRTBSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index dd2b5b5d1e9..3fb1d4d7fa4 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -51,6 +51,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderSomoaudience): syncConfig, string(openrtb_ext.BidderSonobi): syncConfig, string(openrtb_ext.BidderSovrn): syncConfig, + string(openrtb_ext.BidderSmartRTB): syncConfig, string(openrtb_ext.BidderSynacormedia): syncConfig, string(openrtb_ext.BidderTriplelift): syncConfig, string(openrtb_ext.BidderTripleliftNative): syncConfig, From bd43afbca80114e76d1b14a2843b71f8a5f62894 Mon Sep 17 00:00:00 2001 From: CPMStar Date: Thu, 16 Jan 2020 08:14:14 -0800 Subject: [PATCH 003/318] Added new adapter for CPMStar ad network banners and video (#1159) --- adapters/cpmstar/cpmstar.go | 161 ++++++++++++++++++ adapters/cpmstar/cpmstar_test.go | 11 ++ .../exemplary/banner-and-video.json | 154 +++++++++++++++++ .../cpmstar/cpmstartest/exemplary/banner.json | 100 +++++++++++ .../cpmstar/cpmstartest/exemplary/video.json | 55 ++++++ .../cpmstartest/supplemental/audio.json | 25 +++ .../supplemental/explicit-dimensions.json | 58 +++++++ .../invalid-response-no-bids.json | 50 ++++++ .../invalid-response-unmarshall-error.json | 66 +++++++ .../cpmstartest/supplemental/native.json | 25 +++ .../supplemental/no-imps-in-request.json | 18 ++ .../supplemental/server-error-code.json | 53 ++++++ .../supplemental/server-no-content.json | 45 +++++ .../supplemental/wrong-impression-ext.json | 26 +++ .../wrong-impression-mapping.json | 77 +++++++++ adapters/cpmstar/params_test.go | 54 ++++++ adapters/cpmstar/usersync.go | 13 ++ adapters/cpmstar/usersync_test.go | 25 +++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_cpmstar.go | 6 + static/bidder-info/cpmstar.yaml | 11 ++ static/bidder-params/cpmstar.json | 19 +++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 26 files changed, 1061 insertions(+) create mode 100644 adapters/cpmstar/cpmstar.go create mode 100644 adapters/cpmstar/cpmstar_test.go create mode 100644 adapters/cpmstar/cpmstartest/exemplary/banner-and-video.json create mode 100644 adapters/cpmstar/cpmstartest/exemplary/banner.json create mode 100644 adapters/cpmstar/cpmstartest/exemplary/video.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/audio.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/explicit-dimensions.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/invalid-response-no-bids.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/invalid-response-unmarshall-error.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/native.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/no-imps-in-request.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/server-error-code.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/server-no-content.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/wrong-impression-ext.json create mode 100644 adapters/cpmstar/cpmstartest/supplemental/wrong-impression-mapping.json create mode 100644 adapters/cpmstar/params_test.go create mode 100644 adapters/cpmstar/usersync.go create mode 100644 adapters/cpmstar/usersync_test.go create mode 100644 openrtb_ext/imp_cpmstar.go create mode 100644 static/bidder-info/cpmstar.yaml create mode 100644 static/bidder-params/cpmstar.json diff --git a/adapters/cpmstar/cpmstar.go b/adapters/cpmstar/cpmstar.go new file mode 100644 index 00000000000..ef6abe70cb7 --- /dev/null +++ b/adapters/cpmstar/cpmstar.go @@ -0,0 +1,161 @@ +package cpmstar + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type Adapter struct { + endpoint string +} + +func (a *Adapter) MakeRequests(request *openrtb.BidRequest, unused *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var adapterRequests []*adapters.RequestData + + if err := preprocess(request); err != nil { + errs = append(errs, err) + return nil, errs + } + + adapterReq, err := a.makeRequest(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + adapterRequests = append(adapterRequests, adapterReq) + + return adapterRequests, errs +} + +func (a *Adapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, error) { + var err error + + jsonBody, err := json.Marshal(request) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + + return &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: jsonBody, + Headers: headers, + }, nil +} + +func preprocess(request *openrtb.BidRequest) error { + if len(request.Imp) == 0 { + return &errortypes.BadInput{ + Message: "No Imps in Bid Request", + } + } + for i := 0; i < len(request.Imp); i++ { + var imp = &request.Imp[i] + var bidderExt adapters.ExtImpBidder + + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + if err := validateImp(imp); err != nil { + return err + } + + var extImp openrtb_ext.ExtImpCpmstar + if err := json.Unmarshal(bidderExt.Bidder, &extImp); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + imp.Ext = bidderExt.Bidder + } + + return nil +} + +func validateImp(imp *openrtb.Imp) error { + if imp.Banner == nil && imp.Video == nil { + return &errortypes.BadInput{ + Message: "Only Banner and Video bid-types are supported at this time", + } + } + return nil +} + +// MakeBids based on cpmstar server response +func (a *Adapter) MakeBids(bidRequest *openrtb.BidRequest, unused *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected HTTP status code: %d. Run with request.debug = 1 for more info", responseData.StatusCode), + }} + } + + var bidResponse openrtb.BidResponse + + if err := json.Unmarshal(responseData.Body, &bidResponse); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + + if len(bidResponse.SeatBid) == 0 { + return nil, nil + } + + rv := adapters.NewBidderResponseWithBidsCapacity(len(bidResponse.SeatBid[0].Bid)) + var errors []error + + for _, seatbid := range bidResponse.SeatBid { + for _, bid := range seatbid.Bid { + foundMatchingBid := false + bidType := openrtb_ext.BidTypeBanner + for _, imp := range bidRequest.Imp { + if imp.ID == bid.ImpID { + foundMatchingBid = true + if imp.Banner != nil { + bidType = openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + bidType = openrtb_ext.BidTypeVideo + } + break + } + } + + if foundMatchingBid { + rv.Bids = append(rv.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } else { + errors = append(errors, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("bid id='%s' could not find valid impid='%s'", bid.ID, bid.ImpID), + }) + } + } + } + return rv, errors +} + +func NewCpmstarBidder(endpoint string) *Adapter { + return &Adapter{ + endpoint: endpoint, + } +} diff --git a/adapters/cpmstar/cpmstar_test.go b/adapters/cpmstar/cpmstar_test.go new file mode 100644 index 00000000000..0a7f43f5ee7 --- /dev/null +++ b/adapters/cpmstar/cpmstar_test.go @@ -0,0 +1,11 @@ +package cpmstar + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "cpmstartest", NewCpmstarBidder("//host")) +} diff --git a/adapters/cpmstar/cpmstartest/exemplary/banner-and-video.json b/adapters/cpmstar/cpmstartest/exemplary/banner-and-video.json new file mode 100644 index 00000000000..d4dcb4b8677 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/exemplary/banner-and-video.json @@ -0,0 +1,154 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": 154, + "subpoolId": 123 + } + } + }, + { + "id": "test-video-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "placementId": 154, + "subpoolId": 654 + } + } + } + ], + "site": { + "id": "fake-site-id" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "placementId": 154, + "subpoolId": 123 + } + }, + { + "id": "test-video-imp-id", + "video": { + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 640 + }, + "ext": { + "placementId": 154, + "subpoolId": 654 + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "cpmstar", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-video-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "sample.com" + ], + "cid": "958", + "crid": "29681110", + "w": 1024, + "h": 576 + }, + "type": "banner" + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-video-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29484110", + "adomain": [ + "sample.com" + ], + "cid": "958", + "crid": "29484110", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] +} \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/exemplary/banner.json b/adapters/cpmstar/cpmstartest/exemplary/banner.json new file mode 100644 index 00000000000..0bbe3060a63 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/exemplary/banner.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": 154, + "subpoolId": 123 + } + } + } + ], + "site": { + "id": "fake-site-id" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "placementId": 154, + "subpoolId": 123 + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "cpmstar", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-banner-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-banner-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} + \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/exemplary/video.json b/adapters/cpmstar/cpmstartest/exemplary/video.json new file mode 100644 index 00000000000..a0213cbdac1 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/exemplary/video.json @@ -0,0 +1,55 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-video-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 154, + "subpoolId": 123 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-video-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "placementId": 154, + "subpoolId": 123 + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/cpmstar/cpmstartest/supplemental/audio.json b/adapters/cpmstar/cpmstartest/supplemental/audio.json new file mode 100644 index 00000000000..18ff171c7f5 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/audio.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "unsupported-audio-request", + "imp": [ + { + "id": "unsupported-audio-imp", + "audio": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Only Banner and Video bid-types are supported at this time", + "comparison": "literal" + } + ] +} diff --git a/adapters/cpmstar/cpmstartest/supplemental/explicit-dimensions.json b/adapters/cpmstar/cpmstartest/supplemental/explicit-dimensions.json new file mode 100644 index 00000000000..b8aad87514d --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/explicit-dimensions.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "bidder": { + "placementId": 154, + "subpoolId": 123 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "placementId": 154, + "subpoolId": 123 + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/supplemental/invalid-response-no-bids.json b/adapters/cpmstar/cpmstartest/supplemental/invalid-response-no-bids.json new file mode 100644 index 00000000000..a3cb9114caa --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/invalid-response-no-bids.json @@ -0,0 +1,50 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "w": 90, + "h": 728 + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "h": 728, + "w": 90 + }, + "ext": { + "placementId": 154 + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [ + ], + "cur": "USD" + } + } + } + ] +} \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/supplemental/invalid-response-unmarshall-error.json b/adapters/cpmstar/cpmstartest/supplemental/invalid-response-unmarshall-error.json new file mode 100644 index 00000000000..e20acefe2c3 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/invalid-response-unmarshall-error.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "w": 90, + "h": 728 + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "h": 728, + "w": 90 + }, + "ext": { + "placementId": 154 + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [ + { + "bid": [ + { + "id": "uuid", + "impid": "some_test_ad", + "w": "728", + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go struct field Bid(\\.seatbid\\.bid)?\\.w of type uint64", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/supplemental/native.json b/adapters/cpmstar/cpmstartest/supplemental/native.json new file mode 100644 index 00000000000..a02db78db0f --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/native.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "unsupported-native-request", + "imp": [ + { + "id": "unsupported-native-imp", + "native": { + "ver": "1.1" + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Only Banner and Video bid-types are supported at this time", + "comparison": "literal" + } + ] +} diff --git a/adapters/cpmstar/cpmstartest/supplemental/no-imps-in-request.json b/adapters/cpmstar/cpmstartest/supplemental/no-imps-in-request.json new file mode 100644 index 00000000000..274a34227cf --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/no-imps-in-request.json @@ -0,0 +1,18 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + ], + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "No Imps in Bid Request", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/cpmstar/cpmstartest/supplemental/server-error-code.json b/adapters/cpmstar/cpmstartest/supplemental/server-error-code.json new file mode 100644 index 00000000000..21e697d13f1 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/server-error-code.json @@ -0,0 +1,53 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "placementId": 154 + } + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected HTTP status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + } diff --git a/adapters/cpmstar/cpmstartest/supplemental/server-no-content.json b/adapters/cpmstar/cpmstartest/supplemental/server-no-content.json new file mode 100644 index 00000000000..e56db95f8e8 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/server-no-content.json @@ -0,0 +1,45 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": 154 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "placementId": 154 + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] + } diff --git a/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-ext.json b/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-ext.json new file mode 100644 index 00000000000..1e8de0acc66 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-ext.json @@ -0,0 +1,26 @@ +{ + "mockBidRequest": { + "id": "rqid", + "imp": [ + { + "id": "impid", + "video": { + "w": 100, + "h": 200 + }, + "ext": { + "bidder": { + "placementId": "BOGUS" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go struct field ExtImpCpmstar.placementId of type int", + "comparison": "literal" + } + ] +} diff --git a/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-mapping.json b/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-mapping.json new file mode 100644 index 00000000000..6ab02db0ca7 --- /dev/null +++ b/adapters/cpmstar/cpmstartest/supplemental/wrong-impression-mapping.json @@ -0,0 +1,77 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "poolid": 154, + "subpoolid": 123 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "poolid": 154, + "subpoolid": 123 + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "BOGUS-IMPID", + "price": 3.5, + "w": 900, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "bid id='test-bid-id' could not find valid impid='BOGUS-IMPID'", + "comparison": "regex" + } +] +} \ No newline at end of file diff --git a/adapters/cpmstar/params_test.go b/adapters/cpmstar/params_test.go new file mode 100644 index 00000000000..cee471a8322 --- /dev/null +++ b/adapters/cpmstar/params_test.go @@ -0,0 +1,54 @@ +package cpmstar + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/cpmstar.json +// These also validate the format of the external API: request.imp[i].ext.cpmstar +// TestValidParams makes sure that the Cpmstar schema accepts all imp.ext fields which we intend to support. + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderCpmstar, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Cpmstar params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the Cpmstar schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderCpmstar, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placementId": 154}`, + `{"placementId": 154, "subpoolId": 123}`, +} + +var invalidParams = []string{ + `{}`, + `null`, + `true`, + `154`, + `{"placementId": "154"}`, // placementId should be numeric + `{"placementId": 154, "subpoolId": "123"}`, // placementId and subpoolId should both be numeric + `{"invalid_param": 123}`, +} diff --git a/adapters/cpmstar/usersync.go b/adapters/cpmstar/usersync.go new file mode 100644 index 00000000000..9c864e24ce3 --- /dev/null +++ b/adapters/cpmstar/usersync.go @@ -0,0 +1,13 @@ +package cpmstar + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +//NewCpmstarSyncer : +func NewCpmstarSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("cpmstar", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/cpmstar/usersync_test.go b/adapters/cpmstar/usersync_test.go new file mode 100644 index 00000000000..dae55e6302e --- /dev/null +++ b/adapters/cpmstar/usersync_test.go @@ -0,0 +1,25 @@ +package cpmstar + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/stretchr/testify/assert" +) + +func TestCpmstarSyncer(t *testing.T) { + syncURL := "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26us_privacy%3D{{.USPrivacy}}%26uid%3D%24UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewCpmstarSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{}) + + assert.NoError(t, err) + assert.Equal(t, "https://server.cpmstar.com/usersync.aspx?gdpr=&gdpr_consent=&us_privacy=&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D%26gdpr_consent%3D%26us_privacy%3D%26uid%3D%24UID", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.False(t, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 8dc0bb14526..d9b6ee6e55d 100644 --- a/config/config.go +++ b/config/config.go @@ -496,6 +496,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConsumable, "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/prebid/match?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") @@ -678,6 +679,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs") v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2") v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/s2s/header/24") + v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 5795ad2c197..95f5b7f5882 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -22,6 +22,7 @@ import ( "github.com/prebid/prebid-server/adapters/brightroll" "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" + "github.com/prebid/prebid-server/adapters/cpmstar" "github.com/prebid/prebid-server/adapters/datablocks" "github.com/prebid/prebid-server/adapters/emx_digital" "github.com/prebid/prebid-server/adapters/engagebdr" @@ -79,6 +80,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(), openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), + openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index f02a16b4350..7a3f24eb07f 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -33,6 +33,7 @@ const ( BidderBrightroll BidderName = "brightroll" BidderConsumable BidderName = "consumable" BidderConversant BidderName = "conversant" + BidderCpmstar BidderName = "cpmstar" BidderDatablocks BidderName = "datablocks" BidderEmxDigital BidderName = "emx_digital" BidderEngageBDR BidderName = "engagebdr" @@ -87,6 +88,7 @@ var BidderMap = map[string]BidderName{ "brightroll": BidderBrightroll, "consumable": BidderConsumable, "conversant": BidderConversant, + "cpmstar": BidderCpmstar, "datablocks": BidderDatablocks, "emx_digital": BidderEmxDigital, "engagebdr": BidderEngageBDR, diff --git a/openrtb_ext/imp_cpmstar.go b/openrtb_ext/imp_cpmstar.go new file mode 100644 index 00000000000..0b74f4d437d --- /dev/null +++ b/openrtb_ext/imp_cpmstar.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtImpCpmstar struct { + PoolId int `json:"placementId"` + SubPoolId int `json:"subpoolId,omitempty"` +} diff --git a/static/bidder-info/cpmstar.yaml b/static/bidder-info/cpmstar.yaml new file mode 100644 index 00000000000..097dfddd5b0 --- /dev/null +++ b/static/bidder-info/cpmstar.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "prebid@cpmstar.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/cpmstar.json b/static/bidder-params/cpmstar.json new file mode 100644 index 00000000000..576b503e793 --- /dev/null +++ b/static/bidder-params/cpmstar.json @@ -0,0 +1,19 @@ + +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Cpmstar Adapter Params", + "description": "Schema to validate params accepted by the Cpmstar adapter", + + "type": "object", + "properties": { + "placementId": { + "type": "integer", + "description": "Cpmstar-specific ID for ad pool" + }, + "subpoolId": { + "type": "integer", + "description": "Cpmstar-specific ID for ad subpool" + } + }, + "required": ["placementId"] + } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 2bfa5514063..5447cd28800 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -19,6 +19,7 @@ import ( "github.com/prebid/prebid-server/adapters/brightroll" "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" + "github.com/prebid/prebid-server/adapters/cpmstar" "github.com/prebid/prebid-server/adapters/datablocks" "github.com/prebid/prebid-server/adapters/emx_digital" "github.com/prebid/prebid-server/adapters/engagebdr" @@ -75,6 +76,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderBrightroll, brightroll.NewBrightrollSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderConsumable, consumable.NewConsumableSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderCpmstar, cpmstar.NewCpmstarSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderDatablocks, datablocks.NewDatablocksSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderEmxDigital, emx_digital.NewEMXDigitalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 3fb1d4d7fa4..ded8fd2bd78 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -26,6 +26,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderBrightroll): syncConfig, string(openrtb_ext.BidderConsumable): syncConfig, string(openrtb_ext.BidderConversant): syncConfig, + string(openrtb_ext.BidderCpmstar): syncConfig, string(openrtb_ext.BidderDatablocks): syncConfig, string(openrtb_ext.BidderEmxDigital): syncConfig, string(openrtb_ext.BidderEngageBDR): syncConfig, From 85022d14f7d93d58b3b4e64434dec15b629a66fa Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Wed, 22 Jan 2020 21:32:02 -0800 Subject: [PATCH 004/318] Update the Conversant sync pixel (#1161) --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index d9b6ee6e55d..079d5b1f4c7 100644 --- a/config/config.go +++ b/config/config.go @@ -495,7 +495,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConsumable, "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/prebid/match?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/prebid/match/bounce/current?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26networkId%3D72582%26version%3D1%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") From 31c2204793cf0dc6c28ba9a49a861c7b0da4e54c Mon Sep 17 00:00:00 2001 From: rpanchyk Date: Thu, 23 Jan 2020 17:04:18 +0200 Subject: [PATCH 005/318] Add imp.ext.is_rewarded_inventory flag for rewarded video in Rubicon (#1170) --- adapters/rubicon/rubicon.go | 9 ++++++- adapters/rubicon/rubicon_test.go | 42 ++++++++++++++++++++++++++++++++ openrtb_ext/imp.go | 3 +++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index e7461c48f7e..46caf262108 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -129,6 +129,7 @@ type rubiconVideoParams struct { type rubiconVideoExt struct { Skip int `json:"skip,omitempty"` SkipDelay int `json:"skipdelay,omitempty"` + VideoType string `json:"videotype,omitempty"` RP rubiconVideoExtRP `json:"rp"` } @@ -693,8 +694,14 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap continue } + // if imp.ext.is_rewarded_inventory = 1, set imp.video.ext.videotype = "rewarded" + var videoType = "" + if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory == 1 { + videoType = "rewarded" + } + videoCopy := *thisImp.Video - videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, RP: rubiconVideoExtRP{SizeID: rubiconExt.Video.VideoSizeID}} + videoExt := rubiconVideoExt{Skip: rubiconExt.Video.Skip, SkipDelay: rubiconExt.Video.SkipDelay, VideoType: videoType, RP: rubiconVideoExtRP{SizeID: rubiconExt.Video.VideoSizeID}} videoCopy.Ext, err = json.Marshal(&videoExt) thisImp.Video = &videoCopy thisImp.Banner = nil diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index dd9cea62bc7..d386daed5b1 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -1270,6 +1270,48 @@ func TestOpenRTBRequestWithVideoImpEvenIfImpHasBannerButAllRequiredVideoFields(t assert.NotNil(t, rubiconReq.Imp[0].Video, "Video object must be in request impression") } +func TestOpenRTBRequestWithVideoImpAndEnabledRewardedInventoryFlag(t *testing.T) { + bidder := new(RubiconAdapter) + + request := &openrtb.BidRequest{ + ID: "test-request-id", + Imp: []openrtb.Imp{{ + ID: "test-imp-id", + Video: &openrtb.Video{ + W: 640, + H: 360, + MIMEs: []string{"video/mp4"}, + Protocols: []openrtb.Protocol{openrtb.ProtocolVAST10}, + MaxDuration: 30, + Linearity: 1, + API: []openrtb.APIFramework{}, + }, + Ext: json.RawMessage(`{ + "prebid":{ + "is_rewarded_inventory": 1 + }, + "bidder": { + "video": {"size_id": 1} + }}`), + }}, + } + + reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{}) + + rubiconReq := &openrtb.BidRequest{} + if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil { + t.Fatalf("Unexpected error while decoding request: %s", err) + } + + videoExt := &rubiconVideoExt{} + if err := json.Unmarshal(rubiconReq.Imp[0].Video.Ext, &videoExt); err != nil { + t.Fatal("Error unmarshalling request.imp[i].video.ext object.") + } + + assert.Equal(t, "rewarded", videoExt.VideoType, + "Unexpected VideoType. Got %s. Expected %s", videoExt.VideoType, "rewarded") +} + func TestOpenRTBEmptyResponse(t *testing.T) { httpResp := &adapters.ResponseData{ StatusCode: http.StatusNoContent, diff --git a/openrtb_ext/imp.go b/openrtb_ext/imp.go index 499d1f631bf..0e8f224b884 100644 --- a/openrtb_ext/imp.go +++ b/openrtb_ext/imp.go @@ -20,6 +20,9 @@ type ExtImp struct { type ExtImpPrebid struct { StoredRequest *ExtStoredRequest `json:"storedrequest"` + // Rewarded inventory signal, can be 0 or 1 + IsRewardedInventory int8 `json:"is_rewarded_inventory"` + // NOTE: This is not part of the official API, we are not expecting clients // migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER} // at this time From 95200afb3cdcb054f120be21014ae3ff67221cfd Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 23 Jan 2020 16:12:48 +0100 Subject: [PATCH 006/318] [currencies] fix GetInfo() null ref issue (#1169) This CL fixes the null ref on `RateConverter.GetInfo()` when rates are nil. Issue: #1136 --- currencies/rate_converter.go | 6 +++++- currencies/rate_converter_test.go | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/currencies/rate_converter.go b/currencies/rate_converter.go index 63f09bd3c2e..6c6ed172652 100644 --- a/currencies/rate_converter.go +++ b/currencies/rate_converter.go @@ -172,11 +172,15 @@ func (rc *RateConverter) Rates() Conversions { // GetInfo returns setup information about the converter func (rc *RateConverter) GetInfo() ConverterInfo { + var rates *map[string]map[string]float64 + if rc.Rates() != nil { + rates = rc.Rates().GetRates() + } return converterInfo{ source: rc.syncSourceURL, fetchingInterval: rc.fetchingInterval, lastUpdated: rc.LastUpdated(), - rates: rc.Rates().GetRates(), + rates: rates, } } diff --git a/currencies/rate_converter_test.go b/currencies/rate_converter_test.go index f9a14895347..cb5e2a0be54 100644 --- a/currencies/rate_converter_test.go +++ b/currencies/rate_converter_test.go @@ -66,6 +66,7 @@ func TestFetch_Success(t *testing.T) { rates := currencyConverter.Rates() assert.NotNil(t, rates, "Rates() should not return nil") assert.Equal(t, expectedRates, rates, "Rates() doesn't return expected rates") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestFetch_Fail404(t *testing.T) { @@ -92,6 +93,7 @@ func TestFetch_Fail404(t *testing.T) { assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestFetch_FailErrorHttpClient(t *testing.T) { @@ -118,6 +120,7 @@ func TestFetch_FailErrorHttpClient(t *testing.T) { assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestFetch_FailBadSyncURL(t *testing.T) { @@ -134,6 +137,7 @@ func TestFetch_FailBadSyncURL(t *testing.T) { // Verify: assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestFetch_FailBadJSON(t *testing.T) { @@ -174,6 +178,7 @@ func TestFetch_FailBadJSON(t *testing.T) { assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestFetch_InvalidRemoteResponseContent(t *testing.T) { @@ -201,6 +206,7 @@ func TestFetch_InvalidRemoteResponseContent(t *testing.T) { assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestInit(t *testing.T) { @@ -264,6 +270,7 @@ func TestInit(t *testing.T) { assert.NotEqual(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated should be set") rates := currencyConverter.Rates() assert.Equal(t, expectedRates, rates, "Conversions.Rates weren't the expected ones") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") if ticksCount == expectedTicks { currencyConverter.StopPeriodicFetching() @@ -361,6 +368,7 @@ func TestInitWithZeroDuration(t *testing.T) { assert.Equal(t, (time.Time{}), currencyConverter.LastUpdated(), "LastUpdated() shouldn't be set") _, ok := currencyConverter.Rates().(*currencies.ConstantRates) assert.True(t, ok, "Rates should be type of `currencies.ConstantRates`") + assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } func TestRates(t *testing.T) { From 84e2c26cd5df586e2422363ac5398235849d81fe Mon Sep 17 00:00:00 2001 From: Kevin Kerr Date: Fri, 24 Jan 2020 08:37:50 -0500 Subject: [PATCH 007/318] Fix triplelift User Sync (#1173) --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 079d5b1f4c7..282ae9dc2b5 100644 --- a/config/config.go +++ b/config/config.go @@ -525,8 +525,8 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSynacormedia, "https://sync.technoratimedia.com/services?srv=cs&pid=70&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D") // openrtb_ext.BidderTappx doesn't have a good default. - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/sync?gpdr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. From 94cc35467aec9ea6d1abb4943532af0c3a92f6ca Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 27 Jan 2020 14:03:34 -0500 Subject: [PATCH 008/318] Enhance Message For Cache Errors (#1175) --- prebid_cache_client/client.go | 34 ++++++------- prebid_cache_client/client_test.go | 80 ++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 40 deletions(-) diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go index 6da69f68243..58e2734ed25 100644 --- a/prebid_cache_client/client.go +++ b/prebid_cache_client/client.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -92,15 +93,13 @@ func (c *clientImpl) PutJson(ctx context.Context, values []Cacheable) (uuids []s postBody, err := encodeValues(values) if err != nil { - glog.Errorf("Error creating JSON for prebid cache: %v", err) - errs = append(errs, fmt.Errorf("Error creating JSON for prebid cache: %v", err)) + logError(&errs, "Error creating JSON for prebid cache: %v", err) return uuidsToReturn, errs } httpReq, err := http.NewRequest("POST", c.putUrl, bytes.NewReader(postBody)) if err != nil { - glog.Errorf("Error creating POST request to prebid cache: %v", err) - errs = append(errs, fmt.Errorf("Error creating POST request to prebid cache: %v", err)) + logError(&errs, "Error creating POST request to prebid cache: %v", err) return uuidsToReturn, errs } @@ -112,9 +111,7 @@ func (c *clientImpl) PutJson(ctx context.Context, values []Cacheable) (uuids []s elapsedTime := time.Since(startTime) if err != nil { c.metrics.RecordPrebidCacheRequestTime(false, elapsedTime) - friendlyErr := fmt.Errorf("Error sending the request to Prebid Cache: %v; Duration=%v", err, elapsedTime) - glog.Error(friendlyErr) - errs = append(errs, friendlyErr) + logError(&errs, "Error sending the request to Prebid Cache: %v; Duration=%v, Items=%v, Payload Size=%v", err, elapsedTime, len(values), len(postBody)) return uuidsToReturn, errs } defer anResp.Body.Close() @@ -122,23 +119,19 @@ func (c *clientImpl) PutJson(ctx context.Context, values []Cacheable) (uuids []s responseBody, err := ioutil.ReadAll(anResp.Body) if anResp.StatusCode != 200 { - glog.Errorf("Prebid Cache call to %s returned %d: %s", putURL, anResp.StatusCode, responseBody) - errs = append(errs, fmt.Errorf("Prebid Cache call to %s returned %d: %s", putURL, anResp.StatusCode, responseBody)) + logError(&errs, "Prebid Cache call to %s returned %d: %s", putURL, anResp.StatusCode, responseBody) return uuidsToReturn, errs } currentIndex := 0 processResponse := func(uuidObj []byte, _ jsonparser.ValueType, _ int, err error) { if uuid, valueType, _, err := jsonparser.Get(uuidObj, "uuid"); err != nil { - glog.Errorf("Prebid Cache returned a bad value at index %d. Error was: %v. Response body was: %s", currentIndex, err, string(responseBody)) - errs = append(errs, fmt.Errorf("Prebid Cache returned a bad value at index %d. Error was: %v. Response body was: %s", currentIndex, err, string(responseBody))) + logError(&errs, "Prebid Cache returned a bad value at index %d. Error was: %v. Response body was: %s", currentIndex, err, string(responseBody)) } else if valueType != jsonparser.String { - glog.Errorf("Prebid Cache returned a %v at index %d in: %v", valueType, currentIndex, string(responseBody)) - errs = append(errs, fmt.Errorf("Prebid Cache returned a %v at index %d in: %v", valueType, currentIndex, string(responseBody))) + logError(&errs, "Prebid Cache returned a %v at index %d in: %v", valueType, currentIndex, string(responseBody)) } else { if uuidsToReturn[currentIndex], err = jsonparser.ParseString(uuid); err != nil { - glog.Errorf("Prebid Cache response index %d could not be parsed as string: %v", currentIndex, err) - errs = append(errs, fmt.Errorf("Prebid Cache response index %d could not be parsed as string: %v", currentIndex, err)) + logError(&errs, "Prebid Cache response index %d could not be parsed as string: %v", currentIndex, err) uuidsToReturn[currentIndex] = "" } } @@ -146,17 +139,20 @@ func (c *clientImpl) PutJson(ctx context.Context, values []Cacheable) (uuids []s } if _, err := jsonparser.ArrayEach(responseBody, processResponse, "responses"); err != nil { - glog.Errorf("Error interpreting Prebid Cache response: %v\nResponse was: %s", err, string(responseBody)) - errs = append(errs, fmt.Errorf("Error interpreting Prebid Cache response: %v\nResponse was: %s", err, string(responseBody))) + logError(&errs, "Error interpreting Prebid Cache response: %v\nResponse was: %s", err, string(responseBody)) return uuidsToReturn, errs } return uuidsToReturn, errs } +func logError(errs *[]error, format string, a ...interface{}) { + msg := fmt.Sprintf(format, a...) + glog.Error(msg) + *errs = append(*errs, errors.New(msg)) +} + func encodeValues(values []Cacheable) ([]byte, error) { - // This function assumes that m is non-nil and has at least one element. - // clientImp.PutBids should respect this. var buf bytes.Buffer buf.WriteString(`{"puts":[`) for i := 0; i < len(values); i++ { diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go index d3b5ee4bfaf..1b5b4e38967 100644 --- a/prebid_cache_client/client_test.go +++ b/prebid_cache_client/client_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "strconv" @@ -17,7 +18,6 @@ import ( "github.com/stretchr/testify/mock" ) -// Prevents #197 func TestEmptyPut(t *testing.T) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Errorf("The server should not be called.") @@ -72,32 +72,70 @@ func TestBadResponse(t *testing.T) { } func TestCancelledContext(t *testing.T) { - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + testCases := []struct { + description string + cacheable []Cacheable + expectedItems int + expectedPayloadSize int + }{ + { + description: "1 Item", + cacheable: []Cacheable{ + { + Type: TypeJSON, + Data: json.RawMessage("true"), + }, + }, + expectedItems: 1, + expectedPayloadSize: 39, + }, + { + description: "2 Items", + cacheable: []Cacheable{ + { + Type: TypeJSON, + Data: json.RawMessage("true"), + }, + { + Type: TypeJSON, + Data: json.RawMessage("false"), + }, + }, + expectedItems: 2, + expectedPayloadSize: 69, + }, + } + + // Initialize Stub Server + stubHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) - server := httptest.NewServer(handler) - defer server.Close() + stubServer := httptest.NewServer(stubHandler) + defer stubServer.Close() - metricsMock := &pbsmetrics.MetricsEngineMock{} - metricsMock.On("RecordPrebidCacheRequestTime", false, mock.Anything).Once() + // Run Tests + for _, testCase := range testCases { + metricsMock := &pbsmetrics.MetricsEngineMock{} + metricsMock.On("RecordPrebidCacheRequestTime", false, mock.Anything).Once() - client := &clientImpl{ - httpClient: server.Client(), - putUrl: server.URL, - metrics: metricsMock, - } + client := &clientImpl{ + httpClient: stubServer.Client(), + putUrl: stubServer.URL, + metrics: metricsMock, + } - ctx, cancel := context.WithCancel(context.Background()) - cancel() - ids, _ := client.PutJson(ctx, []Cacheable{{ - Type: TypeJSON, - Data: json.RawMessage("true"), - }, - }) - assertIntEqual(t, len(ids), 1) - assertStringEqual(t, ids[0], "") + ctx, cancel := context.WithCancel(context.Background()) + cancel() + ids, errs := client.PutJson(ctx, testCase.cacheable) - metricsMock.AssertExpectations(t) + expectedErrorMessage := fmt.Sprintf("Items=%v, Payload Size=%v", testCase.expectedItems, testCase.expectedPayloadSize) + + assert.Equal(t, testCase.expectedItems, len(ids), testCase.description+":ids") + assert.Len(t, errs, 1) + assert.Contains(t, errs[0].Error(), "Error sending the request to Prebid Cache: context canceled", testCase.description+":error") + assert.Contains(t, errs[0].Error(), expectedErrorMessage, testCase.description+":error_dimensions") + metricsMock.AssertExpectations(t) + } } func TestSuccessfulPut(t *testing.T) { From f75de9240d69d37857af96c13a649dcbc5c0b017 Mon Sep 17 00:00:00 2001 From: PubMatic-OpenWrap Date: Tue, 28 Jan 2020 20:43:46 +0530 Subject: [PATCH 009/318] Fix PubMatic Usersync URL (#1178) Co-authored-by: pm-isha-bharti --- adapters/pubmatic/usersync_test.go | 12 ++++++++---- config/config.go | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/adapters/pubmatic/usersync_test.go b/adapters/pubmatic/usersync_test.go index ef81d223377..dd4a086c453 100644 --- a/adapters/pubmatic/usersync_test.go +++ b/adapters/pubmatic/usersync_test.go @@ -5,12 +5,13 @@ import ( "text/template" "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" "github.com/prebid/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) func TestPubmaticSyncer(t *testing.T) { - syncURL := "//ads.pubmatic.com/AdServer/js/user_sync.html?predirect=localhost%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D" + syncURL := "//ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&predirect=localhost%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D" syncURLTemplate := template.Must( template.New("sync-template").Parse(syncURL), ) @@ -18,13 +19,16 @@ func TestPubmaticSyncer(t *testing.T) { syncer := NewPubmaticSyncer(syncURLTemplate) syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ GDPR: gdpr.Policy{ - Signal: "1", - Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + Signal: "A", + Consent: "B", + }, + CCPA: ccpa.Policy{ + Value: "C", }, }) assert.NoError(t, err) - assert.Equal(t, "//ads.pubmatic.com/AdServer/js/user_sync.html?predirect=localhost%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D", syncInfo.URL) + assert.Equal(t, "//ads.pubmatic.com/AdServer/js/user_sync.html?gdpr=A&gdpr_consent=B&us_privacy=C&predirect=localhost%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D", syncInfo.URL) assert.Equal(t, "iframe", syncInfo.Type) assert.EqualValues(t, 76, syncer.GDPRVendorID()) assert.Equal(t, false, syncInfo.SupportCORS) diff --git a/config/config.go b/config/config.go index 282ae9dc2b5..ba6ed05e339 100644 --- a/config/config.go +++ b/config/config.go @@ -513,7 +513,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderOpenx, "https://rtb.openx.net/sync/prebid?r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderRhythmone, "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BRX_UUID%5D") // openrtb_ext.BidderRTBHouse doesn't have a good default. From 5507b37d96dcfd1dfc2e47efc20bee254bca35b2 Mon Sep 17 00:00:00 2001 From: Corey Kress Date: Fri, 31 Jan 2020 14:09:07 -0500 Subject: [PATCH 010/318] [Synacormedia] Add tagId bidder parameter (#1165) --- adapters/synacormedia/params_test.go | 4 +- adapters/synacormedia/synacormedia.go | 19 ++- .../exemplary/simple-banner.json | 11 +- .../exemplary/simple-video.json | 15 +- .../synacormediatest/params/banner.json | 3 +- .../synacormediatest/params/video.json | 3 +- .../supplemental/audio_response.json | 11 +- .../supplemental/bad_response.json | 11 +- .../supplemental/bad_seat_id.json | 3 +- .../supplemental/missing_seat_id.json | 3 +- .../supplemental/missing_tag_id.json | 30 ++++ .../supplemental/native_response.json | 11 +- .../supplemental/one_bad_ext.json | 136 ++++++++++++++++++ .../supplemental/status_204.json | 11 +- .../supplemental/status_400.json | 11 +- .../supplemental/status_500.json | 11 +- openrtb_ext/imp_synacormedia.go | 1 + static/bidder-params/synacormedia.json | 4 + 18 files changed, 253 insertions(+), 45 deletions(-) create mode 100644 adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json create mode 100644 adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json diff --git a/adapters/synacormedia/params_test.go b/adapters/synacormedia/params_test.go index ffd891f4e84..a216818e382 100644 --- a/adapters/synacormedia/params_test.go +++ b/adapters/synacormedia/params_test.go @@ -40,9 +40,9 @@ func TestInvalidParams(t *testing.T) { } var validParams = []string{ - `{"seatId": "123"}`, + `{"seatId": "123", "tagId":"234"}`, } var invalidParams = []string{ - `{"seatId": 123}`, + `{"seatId": 123, "tagId":234}`, } diff --git a/adapters/synacormedia/synacormedia.go b/adapters/synacormedia/synacormedia.go index ccb2798f8cf..2d913f026b4 100644 --- a/adapters/synacormedia/synacormedia.go +++ b/adapters/synacormedia/synacormedia.go @@ -20,6 +20,7 @@ type SynacorMediaAdapter struct { type SyncEndpointTemplateParams struct { SeatId string + TagId string } type ReqExt struct { @@ -55,14 +56,23 @@ func (a *SynacorMediaAdapter) makeRequest(request *openrtb.BidRequest) (*adapter var firstExtImp *openrtb_ext.ExtImpSynacormedia = nil for _, imp := range request.Imp { - validImp, err := getExtImpObj(&imp) + validExtImpObj, err := getExtImpObj(&imp) // getExtImpObj returns {seatId:"", tagId:""} if err != nil { errs = append(errs, err) continue } + // if the bid request is missing seatId or TagId then ignore it + if validExtImpObj.SeatId == "" || validExtImpObj.TagId == "" { + errs = append(errs, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Invalid Impression"), + }) + continue + } + // right here is where we need to take out the tagId and then add it to imp + imp.TagID = validExtImpObj.TagId validImps = append(validImps, imp) if firstExtImp == nil { - firstExtImp = validImp + firstExtImp = validExtImpObj } } @@ -72,11 +82,12 @@ func (a *SynacorMediaAdapter) makeRequest(request *openrtb.BidRequest) (*adapter var err error - if firstExtImp == nil || firstExtImp.SeatId == "" { + if firstExtImp == nil || firstExtImp.SeatId == "" || firstExtImp.TagId == "" { return nil, append(errs, &errortypes.BadServerResponse{ - Message: fmt.Sprintf("Impression missing seat id"), + Message: fmt.Sprintf("Invalid Impression"), }) } + // this is where the empty seatId is filled re = &ReqExt{SeatId: firstExtImp.SeatId} // create JSON Request Body diff --git a/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json b/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json index 013891b6fa8..944e6e549ab 100644 --- a/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json +++ b/adapters/synacormedia/synacormediatest/exemplary/simple-banner.json @@ -14,7 +14,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -24,15 +25,16 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "banner": { "format": [ {"w":300,"h":250} @@ -40,7 +42,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/exemplary/simple-video.json b/adapters/synacormedia/synacormediatest/exemplary/simple-video.json index f12556105db..2cddd5220f9 100644 --- a/adapters/synacormedia/synacormediatest/exemplary/simple-video.json +++ b/adapters/synacormedia/synacormediatest/exemplary/simple-video.json @@ -10,7 +10,6 @@ "imp": [ { "id": "i3", - "tagid": "3020", "video": { "w": 300, "h": 250, @@ -21,8 +20,9 @@ }, "metric": [], "ext": { - "bidder":{ - "seatId":"1927" + "bidder": { + "seatId":"prebid", + "tagId": "demo1" } } } @@ -31,7 +31,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "1", "site": { @@ -40,12 +40,12 @@ "publisher": {} }, "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id": "i3", - "tagid": "3020", + "tagid": "demo1", "video": { "w": 300, "h": 250, @@ -56,7 +56,8 @@ }, "ext": { "bidder":{ - "seatId":"1927" + "seatId":"prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/params/banner.json b/adapters/synacormedia/synacormediatest/params/banner.json index d6f4e7e9641..bb55ddfc48c 100644 --- a/adapters/synacormedia/synacormediatest/params/banner.json +++ b/adapters/synacormedia/synacormediatest/params/banner.json @@ -1,3 +1,4 @@ { - "seatId": "123" + "seatId": "123", + "tagId": "234" } diff --git a/adapters/synacormedia/synacormediatest/params/video.json b/adapters/synacormedia/synacormediatest/params/video.json index d6f4e7e9641..bb55ddfc48c 100644 --- a/adapters/synacormedia/synacormediatest/params/video.json +++ b/adapters/synacormedia/synacormediatest/params/video.json @@ -1,3 +1,4 @@ { - "seatId": "123" + "seatId": "123", + "tagId": "234" } diff --git a/adapters/synacormedia/synacormediatest/supplemental/audio_response.json b/adapters/synacormedia/synacormediatest/supplemental/audio_response.json index 172a81efa85..752d610aa72 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/audio_response.json +++ b/adapters/synacormedia/synacormediatest/supplemental/audio_response.json @@ -9,7 +9,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -19,21 +20,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "audio": { "mimes": ["video/mp4"] }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/bad_response.json b/adapters/synacormedia/synacormediatest/supplemental/bad_response.json index 0893598bd1d..8e8b9a4d944 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/bad_response.json +++ b/adapters/synacormedia/synacormediatest/supplemental/bad_response.json @@ -18,7 +18,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -28,15 +29,16 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "banner": { "format": [ {"w":300,"h":250}, @@ -45,7 +47,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json b/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json index bb144f1c6db..00dd2c23707 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json +++ b/adapters/synacormedia/synacormediatest/supplemental/bad_seat_id.json @@ -14,7 +14,8 @@ }, "ext": { "bidder": { - "seatId": 1927 + "seatId": 1927, + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json b/adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json index a085b6e64f9..b85b88e4189 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json +++ b/adapters/synacormedia/synacormediatest/supplemental/missing_seat_id.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "tagId": "demo1" } } } @@ -22,7 +23,7 @@ "expectedMakeRequestsErrors": [ { - "value": "Impression missing seat id", + "value": "Invalid Impression", "comparison": "literal" } ] diff --git a/adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json b/adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json new file mode 100644 index 00000000000..2e1ef6ada65 --- /dev/null +++ b/adapters/synacormedia/synacormediatest/supplemental/missing_tag_id.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "seatId": "prebid" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Invalid Impression", + "comparison": "literal" + } + ] +} diff --git a/adapters/synacormedia/synacormediatest/supplemental/native_response.json b/adapters/synacormedia/synacormediatest/supplemental/native_response.json index 89742d6e0df..1428ac1ccd3 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/native_response.json +++ b/adapters/synacormedia/synacormediatest/supplemental/native_response.json @@ -9,7 +9,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -19,21 +20,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "native": { "request": "" }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json b/adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json new file mode 100644 index 00000000000..5749aadba98 --- /dev/null +++ b/adapters/synacormedia/synacormediatest/supplemental/one_bad_ext.json @@ -0,0 +1,136 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-bad", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "seatId": "", + "tagId": "" + } + } + }, + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "seatId": "prebid", + "tagId": "demo1" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", + "body": { + "id": "test-request-id", + "ext": { + "seatId": "prebid" + }, + "imp": [ + { + "id":"test-imp-id", + "tagid": "demo1", + "banner": { + "format": [ + {"w":300,"h":250} + ] + }, + "ext": { + "bidder": { + "seatId": "prebid", + "tagId": "demo1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "1", + "seatbid": [ + { + "bid": [ + { + "id": "test-request-id", + "impid": "test-imp-id", + "price": 2.69, + "adomain": [ + "psacentral.org" + ], + "cid": "mock-crid", + "crid": "mock-cid", + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "synacormedia" + } + ], + "ext": { + "responsetimemillis": { + "synacormedia": 339 + } + } + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adomain": [ + "psacentral.org" + ], + "cid": "mock-crid", + "crid": "mock-cid", + "ext": { + "prebid": { + "type": "banner" + } + }, + "id": "test-request-id", + "impid": "test-imp-id", + "price": 2.69 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "Invalid Impression", + "comparison": "literal" + } + ] +} diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_204.json b/adapters/synacormedia/synacormediatest/supplemental/status_204.json index f53ff1ec918..76f97f9cdfa 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/status_204.json +++ b/adapters/synacormedia/synacormediatest/supplemental/status_204.json @@ -18,7 +18,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -28,15 +29,16 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "banner": { "format": [ {"w":300,"h":250}, @@ -45,7 +47,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_400.json b/adapters/synacormedia/synacormediatest/supplemental/status_400.json index a0667658e1d..1bb2cf6fa45 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/status_400.json +++ b/adapters/synacormedia/synacormediatest/supplemental/status_400.json @@ -18,7 +18,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -28,15 +29,16 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "banner": { "format": [ {"w":300,"h":250}, @@ -45,7 +47,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/adapters/synacormedia/synacormediatest/supplemental/status_500.json b/adapters/synacormedia/synacormediatest/supplemental/status_500.json index 125829c2328..37ca398e59e 100644 --- a/adapters/synacormedia/synacormediatest/supplemental/status_500.json +++ b/adapters/synacormedia/synacormediatest/supplemental/status_500.json @@ -18,7 +18,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } @@ -28,15 +29,16 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://1927.technoratimedia.com/openrtb/bids/1927", + "uri": "http://prebid.technoratimedia.com/openrtb/bids/prebid", "body": { "id": "test-request-id", "ext": { - "seatId": "1927" + "seatId": "prebid" }, "imp": [ { "id":"test-imp-id", + "tagid": "demo1", "banner": { "format": [ {"w":300,"h":250}, @@ -45,7 +47,8 @@ }, "ext": { "bidder": { - "seatId": "1927" + "seatId": "prebid", + "tagId": "demo1" } } } diff --git a/openrtb_ext/imp_synacormedia.go b/openrtb_ext/imp_synacormedia.go index 1b044ceaa9c..af48c7dfd01 100644 --- a/openrtb_ext/imp_synacormedia.go +++ b/openrtb_ext/imp_synacormedia.go @@ -3,4 +3,5 @@ package openrtb_ext // ExtImpSynacormedia defines the contract for bidrequest.imp[i].ext.synacormedia type ExtImpSynacormedia struct { SeatId string `json:"seatId"` + TagId string `json:"tagId"` } diff --git a/static/bidder-params/synacormedia.json b/static/bidder-params/synacormedia.json index b2dff8faca1..8c74ada2e85 100644 --- a/static/bidder-params/synacormedia.json +++ b/static/bidder-params/synacormedia.json @@ -8,6 +8,10 @@ "seatId": { "type": "string", "description": "The seat id." + }, + "tagId": { + "type": "string", + "description": "The tag id." } }, From b8871e98fcae73332be1d2e48c96b09f27d9e87d Mon Sep 17 00:00:00 2001 From: Seba Perez Date: Fri, 31 Jan 2020 17:11:46 -0300 Subject: [PATCH 011/318] Remove all non-secure calls from eplanning adapter (#1179) --- adapters/eplanning/eplanning_test.go | 2 +- .../eplanning/eplanningtest/exemplary/simple-banner-2.json | 2 +- adapters/eplanning/eplanningtest/exemplary/simple-banner.json | 2 +- adapters/eplanning/eplanningtest/exemplary/two-banners.json | 4 ++-- .../eplanningtest/supplemental/banner-no-size-sends-1x1.json | 4 ++-- .../eplanningtest/supplemental/invalid-response-no-bids.json | 4 ++-- .../supplemental/invalid-response-unmarshall-error.json | 4 ++-- .../eplanningtest/supplemental/server-bad-request.json | 4 ++-- .../eplanningtest/supplemental/server-error-code.json | 4 ++-- .../eplanningtest/supplemental/server-no-content.json | 4 ++-- .../supplemental/site-domain-and-url-correctly-parsed.json | 4 ++-- config/config.go | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/adapters/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go index d1219ce4eef..d2c331d456d 100644 --- a/adapters/eplanning/eplanning_test.go +++ b/adapters/eplanning/eplanning_test.go @@ -8,7 +8,7 @@ import ( ) func TestJsonSamples(t *testing.T) { - eplanningAdapter := NewEPlanningBidder(new(http.Client), "http://ads.us.e-planning.net/hb/1") + eplanningAdapter := NewEPlanningBidder(new(http.Client), "https://ads.us.e-planning.net/hb/1") eplanningAdapter.testing = true adapterstest.RunJSONBidderTest(t, "eplanningtest", eplanningAdapter) } diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json index 596b061576f..f4c8fe0c273 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json @@ -28,7 +28,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=300x250:300x250", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=300x250:300x250", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json index 21cbbdae7df..61b7878a8bf 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json @@ -31,7 +31,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de:600x300&uid=2154987&ip=123.123.123.123", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadun_itco_de:600x300&uid=2154987&ip=123.123.123.123", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/two-banners.json b/adapters/eplanning/eplanningtest/exemplary/two-banners.json index 15639524207..fccc1a30e7e 100644 --- a/adapters/eplanning/eplanningtest/exemplary/two-banners.json +++ b/adapters/eplanning/eplanningtest/exemplary/two-banners.json @@ -39,7 +39,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300+300x250:300x250&ip=123.123.123.123", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300+300x250:300x250&ip=123.123.123.123", "body": {} }, "mockResponse": { @@ -120,4 +120,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json index 729115e55de..afa7b9532df 100644 --- a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json +++ b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcodenosize:1x1", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcodenosize:1x1", "body": {} }, "mockResponse": { @@ -67,4 +67,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json index 57db7023360..3bf45a16364 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -45,4 +45,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json index fc85a6b45c0..e8d88f17a5e 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -52,4 +52,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json index 052c0561095..421f47efe3b 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -55,4 +55,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json index 699968ce398..ceec970ba45 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -55,4 +55,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json index 9058699af3e..a2e444a9901 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?r=pbs&ncb=1&ur=FILE&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -30,4 +30,4 @@ } ] } - \ No newline at end of file + diff --git a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json index 4ce8ef2f692..d889f48189c 100644 --- a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json +++ b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?r=pbs&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere&e=testadunitcode:600x300", + "uri": "https://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?r=pbs&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere&e=testadunitcode:600x300", "body": {} }, "mockResponse": { @@ -73,4 +73,4 @@ } ] } - \ No newline at end of file + diff --git a/config/config.go b/config/config.go index ba6ed05e339..2f302cc6328 100644 --- a/config/config.go +++ b/config/config.go @@ -683,7 +683,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") - v.SetDefault("adapters.eplanning.endpoint", "http://ads.us.e-planning.net/hb/1") + v.SetDefault("adapters.eplanning.endpoint", "https://ads.us.e-planning.net/hb/1") v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/") v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io") v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") From 63e7e1df5b11335ed24fed786d6b060eb79d345d Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 5 Feb 2020 14:00:30 -0800 Subject: [PATCH 012/318] Expose Cache HTTP Settings (#1184) --- config/config.go | 4 ++++ config/config_test.go | 7 +++++++ exchange/exchange_test.go | 2 +- prebid_cache_client/client.go | 9 ++------- prebid_cache_client/client_test.go | 4 +--- router/router.go | 18 ++++++++++++++---- 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/config/config.go b/config/config.go index 2f302cc6328..943d18a95de 100644 --- a/config/config.go +++ b/config/config.go @@ -23,6 +23,7 @@ type Configuration struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` Client HTTPClient `mapstructure:"http_client"` + CacheClient HTTPClient `mapstructure:"http_client_cache"` AdminPort int `mapstructure:"admin_port"` EnableGzip bool `mapstructure:"enable_gzip"` // StatusResponse is the string which will be returned by the /status endpoint when things are OK. @@ -583,6 +584,9 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("http_client.max_idle_connections", 400) v.SetDefault("http_client.max_idle_connections_per_host", 10) v.SetDefault("http_client.idle_connection_timeout_seconds", 60) + v.SetDefault("http_client_cache.max_idle_connections", 10) + v.SetDefault("http_client_cache.max_idle_connections_per_host", 2) + v.SetDefault("http_client_cache.idle_connection_timeout_seconds", 60) // no metrics configured by default (metrics{host|database|username|password}) v.SetDefault("metrics.disabled_metrics.account_adapter_details", false) v.SetDefault("metrics.influxdb.host", "") diff --git a/config/config_test.go b/config/config_test.go index 182a46eef50..78630e071d9 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -68,6 +68,10 @@ http_client: max_idle_connections: 500 max_idle_connections_per_host: 20 idle_connection_timeout_seconds: 30 +http_client_cache: + max_idle_connections: 1 + max_idle_connections_per_host: 2 + idle_connection_timeout_seconds: 3 currency_converter: fetch_url: https://currency.prebid.org fetch_interval_seconds: 1800 @@ -214,6 +218,9 @@ func TestFullConfig(t *testing.T) { cmpInts(t, "http_client.max_idle_connections", cfg.Client.MaxIdleConns, 500) cmpInts(t, "http_client.max_idle_connections_per_host", cfg.Client.MaxIdleConnsPerHost, 20) cmpInts(t, "http_client.idle_connection_timeout_seconds", cfg.Client.IdleConnTimeout, 30) + cmpInts(t, "http_client_cache.max_idle_connections", cfg.CacheClient.MaxIdleConns, 1) + cmpInts(t, "http_client_cache.max_idle_connections_per_host", cfg.CacheClient.MaxIdleConnsPerHost, 2) + cmpInts(t, "http_client_cache.idle_connection_timeout_seconds", cfg.CacheClient.IdleConnTimeout, 3) cmpInts(t, "gdpr.host_vendor_id", cfg.GDPR.HostVendorID, 15) cmpBools(t, "gdpr.usersync_if_ambiguous", cfg.GDPR.UsersyncIfAmbiguous, true) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 31dddae4c74..b8a3ae0eae2 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -170,7 +170,7 @@ func TestGetBidCacheInfo(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer)) defer server.Close() - e := NewExchange(server.Client(), pbc.NewClient(&cfg.CacheURL, &cfg.ExtCacheURL, testEngine), cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + e := NewExchange(server.Client(), pbc.NewClient(&http.Client{}, &cfg.CacheURL, &cfg.ExtCacheURL, testEngine), cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) /* 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs */ liveAdapters := []openrtb_ext.BidderName{bidderName} diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go index 58e2734ed25..a5730ce7914 100644 --- a/prebid_cache_client/client.go +++ b/prebid_cache_client/client.go @@ -47,14 +47,9 @@ type Cacheable struct { Key string } -func NewClient(conf *config.Cache, extCache *config.ExternalCache, metrics pbsmetrics.MetricsEngine) Client { +func NewClient(httpClient *http.Client, conf *config.Cache, extCache *config.ExternalCache, metrics pbsmetrics.MetricsEngine) Client { return &clientImpl{ - httpClient: &http.Client{ - Transport: &http.Transport{ - MaxIdleConns: 10, - IdleConnTimeout: 65, - }, - }, + httpClient: httpClient, putUrl: conf.GetBaseURL() + "/cache", externalCacheHost: extCache.Host, externalCachePath: extCache.Path, diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go index 1b5b4e38967..5840d4ea564 100644 --- a/prebid_cache_client/client_test.go +++ b/prebid_cache_client/client_test.go @@ -233,11 +233,9 @@ func TestStripCacheHostAndPath(t *testing.T) { }, } for _, test := range testInput { - //start client - cacheClient := NewClient(&inCacheURL, &test.inExtCacheURL, &metricsConf.DummyMetricsEngine{}) + cacheClient := NewClient(&http.Client{}, &inCacheURL, &test.inExtCacheURL, &metricsConf.DummyMetricsEngine{}) cHost, cPath := cacheClient.GetExtCacheData() - //assert assert.Equal(t, test.expectedHost, cHost) assert.Equal(t, test.expectedPath, cPath) } diff --git a/router/router.go b/router/router.go index 1994639110c..449ab65a448 100644 --- a/router/router.go +++ b/router/router.go @@ -183,7 +183,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r glog.Infof("Could not read certificates file: %s \n", readCertErr.Error()) } - theClient := &http.Client{ + generalHttpClient := &http.Client{ Transport: &http.Transport{ MaxIdleConns: cfg.Client.MaxIdleConns, MaxIdleConnsPerHost: cfg.Client.MaxIdleConnsPerHost, @@ -191,13 +191,22 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r TLSClientConfig: &tls.Config{RootCAs: certPool}, }, } + + cacheHttpClient := &http.Client{ + Transport: &http.Transport{ + MaxIdleConns: cfg.CacheClient.MaxIdleConns, + MaxIdleConnsPerHost: cfg.CacheClient.MaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(cfg.CacheClient.IdleConnTimeout) * time.Second, + }, + } + // Hack because of how legacy handles districtm legacyBidderList := openrtb_ext.BidderList() legacyBidderList = append(legacyBidderList, openrtb_ext.BidderName("districtm")) // Metrics engine r.MetricsEngine = metricsConf.NewMetricsEngine(cfg, legacyBidderList) - db, shutdown, fetcher, ampFetcher, categoriesFetcher, videoFetcher := storedRequestsConf.NewStoredRequests(cfg, r.MetricsEngine, theClient, r.Router) + db, shutdown, fetcher, ampFetcher, categoriesFetcher, videoFetcher := storedRequestsConf.NewStoredRequests(cfg, r.MetricsEngine, generalHttpClient, r.Router) // todo(zachbadgett): better shutdown r.Shutdown = shutdown @@ -223,10 +232,11 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r defaultAliases, defReqJSON := readDefaultRequest(cfg.DefReqConfig) syncers := usersyncers.NewSyncerMap(cfg) - gdprPerms := gdpr.NewPermissions(context.Background(), cfg.GDPR, adapters.GDPRAwareSyncerIDs(syncers), theClient) + gdprPerms := gdpr.NewPermissions(context.Background(), cfg.GDPR, adapters.GDPRAwareSyncerIDs(syncers), generalHttpClient) exchanges = newExchangeMap(cfg) - theExchange := exchange.NewExchange(theClient, pbc.NewClient(&cfg.CacheURL, &cfg.ExtCacheURL, r.MetricsEngine), cfg, r.MetricsEngine, bidderInfos, gdprPerms, rateConvertor) + cacheClient := pbc.NewClient(cacheHttpClient, &cfg.CacheURL, &cfg.ExtCacheURL, r.MetricsEngine) + theExchange := exchange.NewExchange(generalHttpClient, cacheClient, cfg, r.MetricsEngine, bidderInfos, gdprPerms, rateConvertor) openrtbEndpoint, err := openrtb2.NewEndpoint(theExchange, paramsValidator, fetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) From 4ff04cced4bd494e4300269563d201a4d4632dd1 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Thu, 6 Feb 2020 11:19:45 -0800 Subject: [PATCH 013/318] Adding bid rejection messages to debug response (#1181) --- exchange/exchange.go | 43 +++++++----- exchange/exchange_test.go | 139 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 161 insertions(+), 21 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index 3d9055ca8a6..8c6cac4cfcd 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "math/rand" "net/http" @@ -146,10 +147,14 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque //If includebrandcategory is present in ext then CE feature is on. if requestExt.Prebid.Targeting != nil && requestExt.Prebid.Targeting.IncludeBrandCategory != nil { var err error - bidCategory, adapterBids, err = applyCategoryMapping(ctx, requestExt, adapterBids, *categoriesFetcher, targData) + var rejections []string + bidCategory, adapterBids, rejections, err = applyCategoryMapping(ctx, requestExt, adapterBids, *categoriesFetcher, targData) if err != nil { return nil, fmt.Errorf("Error in category mapping : %s", err.Error()) } + for _, message := range rejections { + errs = append(errs, errors.New(message)) + } } auc = newAuction(adapterBids, len(bidRequest.Imp)) @@ -340,7 +345,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ return bidResponse, err } -func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest, seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, categoriesFetcher stored_requests.CategoryFetcher, targData *targetData) (map[string]string, map[openrtb_ext.BidderName]*pbsOrtbSeatBid, error) { +func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest, seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, categoriesFetcher stored_requests.CategoryFetcher, targData *targetData) (map[string]string, map[openrtb_ext.BidderName]*pbsOrtbSeatBid, []string, error) { res := make(map[string]string) type bidDedupe struct { @@ -359,6 +364,7 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest var primaryAdServer string var publisher string var err error + var rejections []string var translateCategories = true if includeBrandCategory && brandCatExt.WithCategory { @@ -370,7 +376,7 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest //if ext.prebid.targeting.includebrandcategory present but primaryadserver/publisher not present then error out the request right away. primaryAdServer, err = getPrimaryAdServer(brandCatExt.PrimaryAdServer) //1-Freewheel 2-DFP if err != nil { - return res, seatBids, err + return res, seatBids, rejections, err } publisher = brandCatExt.Publisher } @@ -382,6 +388,7 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest bidsToRemove := make([]int, 0) for bidInd := range seatBid.bids { bid := seatBid.bids[bidInd] + bidID := bid.bid.ID var duration int var category string var pb string @@ -396,6 +403,7 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest //TODO: add metrics //on receiving bids from adapters if no unique IAB category is returned or if no ad server category is returned discard the bid bidsToRemove = append(bidsToRemove, bidInd) + rejections = updateRejections(rejections, bidID, "Bid did not contain a category") continue } if translateCategories { @@ -405,6 +413,8 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest //TODO: add metrics //if mapping required but no mapping file is found then discard the bid bidsToRemove = append(bidsToRemove, bidInd) + reason := fmt.Sprintf("Category mapping file for primary ad server: '%s', publisher: '%s' not found", primaryAdServer, publisher) + rejections = updateRejections(rejections, bidID, reason) continue } } else { @@ -424,6 +434,7 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest //if the bid is above the range of the listed durations (and outside the buffer), reject the bid if duration > durationRange[len(durationRange)-1] { bidsToRemove = append(bidsToRemove, bidInd) + rejections = updateRejections(rejections, bidID, "Bid duration exceeds maximum allowed") continue } for _, dur := range durationRange { @@ -447,11 +458,13 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest if dupe.bidderName == bidderName { // An older bid from the current bidder bidsToRemove = append(bidsToRemove, dupe.bidIndex) + rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") } else { // An older bid from a different seatBid we've already finished with oldSeatBid := (seatBids)[dupe.bidderName] if len(oldSeatBid.bids) == 1 { seatBidsToRemove = append(seatBidsToRemove, bidderName) + rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") } else { oldSeatBid.bids = append(oldSeatBid.bids[:dupe.bidIndex], oldSeatBid.bids[dupe.bidIndex+1:]...) } @@ -460,11 +473,12 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest } else { // Remove this bid bidsToRemove = append(bidsToRemove, bidInd) + rejections = updateRejections(rejections, bidID, "Bid was deduplicated") continue } } - res[bid.bid.ID] = categoryDuration - dedupe[categoryDuration] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bid.bid.ID} + res[bidID] = categoryDuration + dedupe[categoryDuration] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID} } if len(bidsToRemove) > 0 { @@ -483,19 +497,16 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest } } - if len(seatBidsToRemove) > 0 { - if len(seatBidsToRemove) == len(seatBids) { - //delete all seat bids - seatBids = nil - } else { - for _, seatBidInd := range seatBidsToRemove { - delete(seatBids, seatBidInd) - } - - } + for _, seatBidInd := range seatBidsToRemove { + seatBids[seatBidInd].bids = nil } - return res, seatBids, nil + return res, seatBids, rejections, nil +} + +func updateRejections(rejections []string, bidID string, reason string) []string { + message := fmt.Sprintf("bid rejected [bid ID: %s] reason: %s", bidID, reason) + return append(rejections, message) } func getPrimaryAdServer(adServerId int) (string, error) { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index b8a3ae0eae2..7e199d4b750 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "regexp" "strconv" "strings" "testing" @@ -930,9 +931,11 @@ func TestCategoryMapping(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Equal(t, 1, len(rejections), "There should be 1 bid rejection message") + assert.Equal(t, "bid rejected [bid ID: bid_id4] reason: Category mapping file for primary ad server: 'freewheel', publisher: '' not found", rejections[0], "Rejection message did not match expected") assert.Equal(t, "10.00_Electronics_30s", bidCategory["bid_id1"], "Category mapping doesn't match") assert.Equal(t, "20.00_Sports_50s", bidCategory["bid_id2"], "Category mapping doesn't match") assert.Equal(t, "20.00_AdapterOverride_30s", bidCategory["bid_id3"], "Category mapping override from adapter didn't take") @@ -983,9 +986,10 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Empty(t, rejections, "There should be no bid rejection messages") assert.Equal(t, "10.00_30s", bidCategory["bid_id1"], "Category mapping doesn't match") assert.Equal(t, "20.00_40s", bidCategory["bid_id2"], "Category mapping doesn't match") assert.Equal(t, "20.00_30s", bidCategory["bid_id3"], "Category mapping doesn't match") @@ -1034,9 +1038,11 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Equal(t, 1, len(rejections), "There should be 1 bid rejection message") + assert.Equal(t, "bid rejected [bid ID: bid_id3] reason: Category mapping file for primary ad server: 'freewheel', publisher: '' not found", rejections[0], "Rejection message did not match expected") assert.Equal(t, "10.00_Electronics_30s", bidCategory["bid_id1"], "Category mapping doesn't match") assert.Equal(t, "20.00_Sports_50s", bidCategory["bid_id2"], "Category mapping doesn't match") assert.Equal(t, 2, len(adapterBids[bidderName1].bids), "Bidders number doesn't match") @@ -1114,9 +1120,10 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Empty(t, rejections, "There should be no bid rejection messages") assert.Equal(t, "10.00_IAB1-3_30s", bidCategory["bid_id1"], "Category should not be translated") assert.Equal(t, "20.00_IAB1-4_50s", bidCategory["bid_id2"], "Category should not be translated") assert.Equal(t, "20.00_IAB1-1000_30s", bidCategory["bid_id3"], "Bid should not be rejected") @@ -1179,9 +1186,12 @@ func TestCategoryDedupe(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Equal(t, 2, len(rejections), "There should be 2 bid rejection messages") + assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_id(1|3)\] reason: Bid was deduplicated`), rejections[0], "Rejection message did not match expected") + assert.Equal(t, "bid rejected [bid ID: bid_id4] reason: Category mapping file for primary ad server: 'freewheel', publisher: '' not found", rejections[1], "Rejection message did not match expected") assert.Equal(t, 2, len(adapterBids[bidderName1].bids), "Bidders number doesn't match") assert.Equal(t, 2, len(bidCategory), "Bidders category mapping doesn't match") @@ -1196,6 +1206,125 @@ func TestCategoryDedupe(t *testing.T) { assert.NotEqual(t, numIterations, selectedBids["bid_id3"], "Bid 3 made it through every time") } +func TestBidRejectionErrors(t *testing.T) { + categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") + if error != nil { + t.Errorf("Failed to create a category Fetcher: %v", error) + } + + requestExt := newExtRequest() + requestExt.Prebid.Targeting.DurationRangeSec = []int{15, 30, 50} + + targData := &targetData{ + priceGranularity: requestExt.Prebid.Targeting.PriceGranularity, + includeWinners: true, + } + + invalidReqExt := newExtRequest() + invalidReqExt.Prebid.Targeting.DurationRangeSec = []int{15, 30, 50} + invalidReqExt.Prebid.Targeting.IncludeBrandCategory.PrimaryAdServer = 2 + invalidReqExt.Prebid.Targeting.IncludeBrandCategory.Publisher = "some_publisher" + + adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid) + bidderName := openrtb_ext.BidderName("appnexus") + + testCases := []struct { + description string + reqExt openrtb_ext.ExtRequest + bids []*openrtb.Bid + duration int + expectedRejections []string + expectedCatDur string + }{ + { + description: "Bid should be rejected due to not containing a category", + reqExt: requestExt, + bids: []*openrtb.Bid{ + {ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{}, W: 1, H: 1}, + }, + duration: 30, + expectedRejections: []string{ + "bid rejected [bid ID: bid_id1] reason: Bid did not contain a category", + }, + }, + { + description: "Bid should be rejected due to missing category mapping file", + reqExt: invalidReqExt, + bids: []*openrtb.Bid{ + {ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{"IAB1-1"}, W: 1, H: 1}, + }, + duration: 30, + expectedRejections: []string{ + "bid rejected [bid ID: bid_id1] reason: Category mapping file for primary ad server: 'dfp', publisher: 'some_publisher' not found", + }, + }, + { + description: "Bid should be rejected due to duration exceeding maximum", + reqExt: requestExt, + bids: []*openrtb.Bid{ + {ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{"IAB1-1"}, W: 1, H: 1}, + }, + duration: 70, + expectedRejections: []string{ + "bid rejected [bid ID: bid_id1] reason: Bid duration exceeds maximum allowed", + }, + }, + { + description: "Bid should be rejected due to duplicate bid", + reqExt: requestExt, + bids: []*openrtb.Bid{ + {ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{"IAB1-1"}, W: 1, H: 1}, + {ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{"IAB1-1"}, W: 1, H: 1}, + }, + duration: 30, + expectedRejections: []string{ + "bid rejected [bid ID: bid_id1] reason: Bid was deduplicated", + }, + expectedCatDur: "10.00_VideoGames_30s", + }, + } + + for _, test := range testCases { + innerBids := []*pbsOrtbBid{} + for _, bid := range test.bids { + currentBid := pbsOrtbBid{ + bid, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, + } + innerBids = append(innerBids, ¤tBid) + } + + seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + + adapterBids[bidderName] = &seatBid + + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, test.reqExt, adapterBids, categoriesFetcher, targData) + + if len(test.expectedCatDur) > 0 { + // Bid deduplication case + assert.Equal(t, 1, len(adapterBids[bidderName].bids), "Bidders number doesn't match") + assert.Equal(t, 1, len(bidCategory), "Bidders category mapping doesn't match") + assert.Equal(t, test.expectedCatDur, bidCategory["bid_id1"], "Bid category did not contain expected hb_pb_cat_dur") + } else { + assert.Empty(t, adapterBids[bidderName].bids, "Bidders number doesn't match") + assert.Empty(t, bidCategory, "Bidders category mapping doesn't match") + } + + assert.Empty(t, err, "Category mapping error should be empty") + assert.Equal(t, test.expectedRejections, rejections, test.description) + } +} + +func TestUpdateRejections(t *testing.T) { + rejections := []string{} + + rejections = updateRejections(rejections, "bid_id1", "some reason 1") + rejections = updateRejections(rejections, "bid_id2", "some reason 2") + + assert.Equal(t, 2, len(rejections), "Rejections should contain 2 rejection messages") + assert.Containsf(t, rejections, "bid rejected [bid ID: bid_id1] reason: some reason 1", "Rejection message did not match expected") + assert.Containsf(t, rejections, "bid rejected [bid ID: bid_id2] reason: some reason 2", "Rejection message did not match expected") +} + type exchangeSpec struct { IncomingRequest exchangeRequest `json:"incomingRequest"` OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` From fd9bc8f11aa528ef900bd9fd9f63c165acd93a40 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Mon, 10 Feb 2020 16:38:49 -0500 Subject: [PATCH 014/318] Adds timeout notifications for Facebook (#1182) --- adapters/adpone/adpone.go | 3 +- adapters/audienceNetwork/facebook.go | 17 +++++++++++ adapters/audienceNetwork/facebook_test.go | 37 +++++++++++++++++++++++ adapters/bidder.go | 13 ++++++++ exchange/bidder.go | 24 +++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/adapters/adpone/adpone.go b/adapters/adpone/adpone.go index 345a4988580..b1822a0ac07 100644 --- a/adapters/adpone/adpone.go +++ b/adapters/adpone/adpone.go @@ -3,9 +3,10 @@ package adpone import ( "encoding/json" "fmt" - "github.com/prebid/prebid-server/openrtb_ext" "net/http" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index 706673cbafc..3ece7bb99e4 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -447,3 +447,20 @@ func NewFacebookBidder(client *http.Client, platformID string, appSecret string) appSecret: appSecret, } } + +func (fa *FacebookAdapter) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error) { + // Note, facebook creates one request per imp, so all these requests will only have one imp in them + auction_id, err := jsonparser.GetString(req.Body, "imp", "[0]", "id") + if err != nil { + return &adapters.RequestData{}, []error{err} + } + + uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", fa.platformID, fa.platformID, auction_id) + timeoutReq := adapters.RequestData{ + Method: "GET", + Uri: uri, + Body: nil, + Headers: http.Header{}, + } + return &timeoutReq, nil +} diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go index 2ce0ef3ba64..1edaabd45d7 100644 --- a/adapters/audienceNetwork/facebook_test.go +++ b/adapters/audienceNetwork/facebook_test.go @@ -4,7 +4,9 @@ import ( "testing" "time" + "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/stretchr/testify/assert" ) type tagInfo struct { @@ -40,3 +42,38 @@ type FacebookExt struct { func TestJsonSamples(t *testing.T) { adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder(nil, "test-platform-id", "test-app-secret")) } + +func TestMakeTimeoutNotice(t *testing.T) { + req := adapters.RequestData{ + Body: []byte(`{"imp":[{"id":"1234"}]}}`), + } + fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + + tb, ok := fba.(adapters.TimeoutBidder) + if !ok { + t.Error("Facebook adapter is not a TimeoutAdapter") + } + + toReq, err := tb.MakeTimeoutNotification(&req) + assert.Nil(t, err, "Facebook MakeTimeoutNotification() return an error %v", err) + expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=test-platform-id&auction=1234&ortb_loss_code=2" + assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.") + +} + +func TestMakeTimeoutNoticeBadRequest(t *testing.T) { + req := adapters.RequestData{ + Body: []byte(`{"imp":[{{"id":"1234"}}`), + } + fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + + tb, ok := fba.(adapters.TimeoutBidder) + if !ok { + t.Error("Facebook adapter is not a TimeoutAdapter") + } + + toReq, err := tb.MakeTimeoutNotification(&req) + assert.Empty(t, toReq.Uri, "Facebook MakeTimeoutNotification() did not return nil", err) + assert.NotNil(t, err, "Facebook MakeTimeoutNotification() did not return an error") + +} diff --git a/adapters/bidder.go b/adapters/bidder.go index 9d3ffb75414..baec4135b6a 100644 --- a/adapters/bidder.go +++ b/adapters/bidder.go @@ -39,6 +39,19 @@ type Bidder interface { MakeBids(internalRequest *openrtb.BidRequest, externalRequest *RequestData, response *ResponseData) (*BidderResponse, []error) } +// TimeoutBidder is used to identify bidders that support timeout notifications. +type TimeoutBidder interface { + Bidder + + // MakeTimeoutNotice functions much the same as MakeRequests, except it is fed the bidder request that timed out, + // and expects that only one notification "request" will be generated. A use case for multiple timeout notifications + // has not been anticipated. + // + // Do note that if MakeRequests returns multiple requests, and more than one of these times out, MakeTimeoutNotice will be called + // once for each timed out request. + MakeTimeoutNotification(req *RequestData) (*RequestData, []error) +} + type MisconfiguredBidder struct { Name string Error error diff --git a/exchange/bidder.go b/exchange/bidder.go index 5708660057f..d9a28fee175 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "net/http" + "time" "github.com/mxmCherry/openrtb" nativeRequests "github.com/mxmCherry/openrtb/native/request" @@ -295,6 +296,14 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques if err != nil { if err == context.DeadlineExceeded { err = &errortypes.Timeout{Message: err.Error()} + if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); ok { + // Toss the timeout notification call into a go routine, as we are out of time' + // and cannot delay processing. We don't do anything result, as there is not much + // we can do about a timeout notification failure. We do not want to get stuck in + // a loop of trying to report timeouts to the timeout notifications. + go bidder.doTimeoutNotification(tb, req) + } + } return &httpCallInfo{ request: req, @@ -328,6 +337,21 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques } } +func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.TimeoutBidder, req *adapters.RequestData) { + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + defer cancel() + toReq, errL := timeoutBidder.MakeTimeoutNotification(req) + if toReq != nil && len(errL) == 0 { + httpReq, err := http.NewRequest(toReq.Method, toReq.Uri, bytes.NewBuffer(toReq.Body)) + if err == nil { + httpReq.Header = req.Headers + ctxhttp.Do(ctx, bidder.Client, httpReq) + // No validation yet on sending notifications + } + } + +} + type httpCallInfo struct { request *adapters.RequestData response *adapters.ResponseData From 7762c0c3a926c07b24ef50605545a4987cd4e280 Mon Sep 17 00:00:00 2001 From: Michael Kuryshev Date: Tue, 18 Feb 2020 18:35:49 +0100 Subject: [PATCH 015/318] VIS.X: added app type support (#1194) --- static/bidder-info/visx.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/bidder-info/visx.yaml b/static/bidder-info/visx.yaml index dd4f6c660de..f404a013337 100644 --- a/static/bidder-info/visx.yaml +++ b/static/bidder-info/visx.yaml @@ -4,3 +4,6 @@ capabilities: site: mediaTypes: - banner + app: + mediaTypes: + - banner From 8e382e7b8c12b66e4ce6392078b00c2151ce6f32 Mon Sep 17 00:00:00 2001 From: Viacheslav Chimishuk Date: Fri, 21 Feb 2020 20:14:53 +0200 Subject: [PATCH 016/318] Add Adoppler bidder support. (#1186) * Add Adoppler bidder support. * Address code review comments. Use JSON-templates for testing. * Fix misprint; Add url.PathEscape call for adunit URL parameter. --- adapters/adoppler/adoppler.go | 210 ++++++++++++++++++ adapters/adoppler/adoppler_test.go | 12 + .../adopplertest/exemplary/multibid.json | 60 +++++ .../adopplertest/exemplary/no-bid.json | 13 ++ .../supplemental/bad-request.json | 15 ++ .../supplemental/duplicate-imp.json | 38 ++++ .../supplemental/invalid-impid.json | 20 ++ .../supplemental/invalid-response.json | 15 ++ .../supplemental/invalid-video-ext.json | 43 ++++ .../supplemental/missing-adunit.json | 9 + .../supplemental/server-error.json | 15 ++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adoppler.go | 5 + static/bidder-info/adoppler.yaml | 11 + static/bidder-params/adoppler.json | 13 ++ usersync/usersyncers/syncer_test.go | 1 + 18 files changed, 485 insertions(+) create mode 100644 adapters/adoppler/adoppler.go create mode 100644 adapters/adoppler/adoppler_test.go create mode 100644 adapters/adoppler/adopplertest/exemplary/multibid.json create mode 100644 adapters/adoppler/adopplertest/exemplary/no-bid.json create mode 100644 adapters/adoppler/adopplertest/supplemental/bad-request.json create mode 100644 adapters/adoppler/adopplertest/supplemental/duplicate-imp.json create mode 100644 adapters/adoppler/adopplertest/supplemental/invalid-impid.json create mode 100644 adapters/adoppler/adopplertest/supplemental/invalid-response.json create mode 100644 adapters/adoppler/adopplertest/supplemental/invalid-video-ext.json create mode 100644 adapters/adoppler/adopplertest/supplemental/missing-adunit.json create mode 100644 adapters/adoppler/adopplertest/supplemental/server-error.json create mode 100644 openrtb_ext/imp_adoppler.go create mode 100644 static/bidder-info/adoppler.yaml create mode 100644 static/bidder-params/adoppler.json diff --git a/adapters/adoppler/adoppler.go b/adapters/adoppler/adoppler.go new file mode 100644 index 00000000000..c604bfeac06 --- /dev/null +++ b/adapters/adoppler/adoppler.go @@ -0,0 +1,210 @@ +package adoppler + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +var bidHeaders http.Header = map[string][]string{ + "Accept": {"application/json"}, + "Content-Type": {"application/json;charset=utf-8"}, + "X-OpenRTB-Version": {"2.5"}, +} + +type adsVideoExt struct { + Duration int `json:"duration"` +} + +type adsImpExt struct { + Video *adsVideoExt `json:"video"` +} + +type AdopplerAdapter struct { + endpoint string +} + +func NewAdopplerBidder(endpoint string) *AdopplerAdapter { + return &AdopplerAdapter{endpoint} +} + +func (ads *AdopplerAdapter) MakeRequests( + req *openrtb.BidRequest, + info *adapters.ExtraRequestInfo, +) ( + []*adapters.RequestData, + []error, +) { + if len(req.Imp) == 0 { + return nil, nil + } + + var datas []*adapters.RequestData + var errs []error + for _, imp := range req.Imp { + ext, err := unmarshalExt(imp.Ext) + if err != nil { + errs = append(errs, &errortypes.BadInput{err.Error()}) + continue + } + + var r openrtb.BidRequest = *req + r.ID = req.ID + "-" + ext.AdUnit + r.Imp = []openrtb.Imp{imp} + + body, err := json.Marshal(r) + if err != nil { + errs = append(errs, err) + continue + } + + uri := fmt.Sprintf("%s/processHeaderBid/%s", + ads.endpoint, url.PathEscape(ext.AdUnit)) + data := &adapters.RequestData{ + Method: "POST", + Uri: uri, + Body: body, + Headers: bidHeaders, + } + datas = append(datas, data) + } + + return datas, errs +} + +func (ads *AdopplerAdapter) MakeBids( + intReq *openrtb.BidRequest, + extReq *adapters.RequestData, + resp *adapters.ResponseData, +) ( + *adapters.BidderResponse, + []error, +) { + if resp.StatusCode == http.StatusNoContent { + return nil, nil + } + if resp.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{"bad request"}} + } + if resp.StatusCode != http.StatusOK { + err := &errortypes.BadServerResponse{ + fmt.Sprintf("unexpected status: %d", resp.StatusCode), + } + return nil, []error{err} + } + + var bidResp openrtb.BidResponse + err := json.Unmarshal(resp.Body, &bidResp) + if err != nil { + err := &errortypes.BadServerResponse{ + fmt.Sprintf("invalid body: %s", err.Error()), + } + return nil, []error{err} + } + + impTypes := make(map[string]openrtb_ext.BidType) + for _, imp := range intReq.Imp { + if _, ok := impTypes[imp.ID]; ok { + return nil, []error{&errortypes.BadInput{ + fmt.Sprintf("duplicate $.imp.id %s", imp.ID), + }} + } + if imp.Banner != nil { + impTypes[imp.ID] = openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + impTypes[imp.ID] = openrtb_ext.BidTypeVideo + } else if imp.Audio != nil { + impTypes[imp.ID] = openrtb_ext.BidTypeAudio + } else if imp.Native != nil { + impTypes[imp.ID] = openrtb_ext.BidTypeNative + } else { + return nil, []error{&errortypes.BadInput{ + "one of $.imp.banner, $.imp.video, $.imp.audio and $.imp.native field required", + }} + } + } + + var bids []*adapters.TypedBid + for _, seatBid := range bidResp.SeatBid { + for _, bid := range seatBid.Bid { + tp, ok := impTypes[bid.ImpID] + if !ok { + err := &errortypes.BadServerResponse{ + fmt.Sprintf("unknown impid: %s", bid.ImpID), + } + return nil, []error{err} + } + + var bidVideo *openrtb_ext.ExtBidPrebidVideo + if tp == openrtb_ext.BidTypeVideo { + adsExt, err := unmarshalAdsExt(bid.Ext) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{err.Error()}} + } + if adsExt == nil || adsExt.Video == nil { + return nil, []error{&errortypes.BadServerResponse{ + "$.seatbid.bid.ext.ads.video required", + }} + } + bidVideo = &openrtb_ext.ExtBidPrebidVideo{ + Duration: adsExt.Video.Duration, + PrimaryCategory: head(bid.Cat), + } + } + bids = append(bids, &adapters.TypedBid{ + Bid: &bid, + BidType: tp, + BidVideo: bidVideo, + }) + } + } + + adsResp := adapters.NewBidderResponseWithBidsCapacity(len(bids)) + adsResp.Bids = bids + + return adsResp, nil +} + +func unmarshalExt(ext json.RawMessage) (*openrtb_ext.ExtImpAdoppler, error) { + var bext adapters.ExtImpBidder + err := json.Unmarshal(ext, &bext) + if err != nil { + return nil, err + } + + var adsExt openrtb_ext.ExtImpAdoppler + err = json.Unmarshal(bext.Bidder, &adsExt) + if err != nil { + return nil, err + } + + if adsExt.AdUnit == "" { + return nil, errors.New("$.imp.ext.adoppler.adunit required") + } + + return &adsExt, nil +} + +func unmarshalAdsExt(ext json.RawMessage) (*adsImpExt, error) { + var e struct { + Ads *adsImpExt `json:"ads"` + } + err := json.Unmarshal(ext, &e) + + return e.Ads, err +} + +func head(s []string) string { + if len(s) == 0 { + return "" + } + + return s[0] +} diff --git a/adapters/adoppler/adoppler_test.go b/adapters/adoppler/adoppler_test.go new file mode 100644 index 00000000000..e7d908df4f1 --- /dev/null +++ b/adapters/adoppler/adoppler_test.go @@ -0,0 +1,12 @@ +package adoppler + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + bidder := NewAdopplerBidder("http://adoppler.com") + adapterstest.RunJSONBidderTest(t, "adopplertest", bidder) +} diff --git a/adapters/adoppler/adopplertest/exemplary/multibid.json b/adapters/adoppler/adopplertest/exemplary/multibid.json new file mode 100644 index 00000000000..851f4c5b917 --- /dev/null +++ b/adapters/adoppler/adopplertest/exemplary/multibid.json @@ -0,0 +1,60 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}, + {"id": "imp2", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit2"}}}, + {"id": "imp3", + "native": {"request": "{}"}, + "ext": {"bidder": {"adunit": "unit3"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp1-resp1", + "seatbid": [{"bid": [{"id": "req1-imp1-bid1", + "impid": "imp1", + "price": 0.12, + "adm": "a banner"}]}], + "cur": "USD"}}}, + {"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit2", + "body": {"id": "req1-unit2", + "imp": [{"id": "imp2", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit2"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp2-resp2", + "seatbid": [{"bid": [{"id": "req1-imp2-bid1", + "impid": "imp2", + "price": 0.24, + "adm": "", + "cat": ["IAB1", "IAB2"], + "ext": {"ads": {"video": {"duration": 121}}}}]}], + "cur": "USD"}}}, + {"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit3", + "body": {"id": "req1-unit3", + "imp": [{"id": "imp3", + "native": {"request": "{}"}, + "ext": {"bidder": {"adunit": "unit3"}}}]}}, + "mockResponse": {"status": 204, + "body": ""}}], + "expectedBidResponses": [{"currency": "USD", + "bids": [{"bid": {"id": "req1-imp1-bid1", + "impid": "imp1", + "price": 0.12, + "adm": "a banner"}, + "type": "banner"}]}, + {"currency": "USD", + "bids": [{"bid": {"id": "req1-imp2-bid1", + "impid": "imp2", + "price": 0.24, + "adm": "", + "cat": ["IAB1", "IAB2"], + "ext": {"ads": {"video": {"duration": 121}}}}, + "type": "video"}]}]} diff --git a/adapters/adoppler/adopplertest/exemplary/no-bid.json b/adapters/adoppler/adopplertest/exemplary/no-bid.json new file mode 100644 index 00000000000..0e0f13586a8 --- /dev/null +++ b/adapters/adoppler/adopplertest/exemplary/no-bid.json @@ -0,0 +1,13 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 204, + "body": ""}}], + "expectedBidResponses": []} diff --git a/adapters/adoppler/adopplertest/supplemental/bad-request.json b/adapters/adoppler/adopplertest/supplemental/bad-request.json new file mode 100644 index 00000000000..3bdd5a5544e --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/bad-request.json @@ -0,0 +1,15 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 400, + "body": ""}}], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{"value": "bad request", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/duplicate-imp.json b/adapters/adoppler/adopplertest/supplemental/duplicate-imp.json new file mode 100644 index 00000000000..4382e36c54e --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/duplicate-imp.json @@ -0,0 +1,38 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}, + {"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit2"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp1-resp1", + "seatbid": [{"bid": [{"id": "req1-imp1-bid1", + "impid": "imp1", + "price": 0.12, + "adm": "a banner"}]}], + "cur": "USD"}}}, + {"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit2", + "body": {"id": "req1-unit2", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit2"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp1-resp1", + "seatbid": [{"bid": [{"id": "req1-imp1-bid1", + "impid": "imp1", + "price": 0.12, + "adm": "a banner"}]}], + "cur": "USD"}}}], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{"value": "duplicate $.imp.id imp1", + "comparison": "literal"}, + {"value": "duplicate $.imp.id imp1", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/invalid-impid.json b/adapters/adoppler/adopplertest/supplemental/invalid-impid.json new file mode 100644 index 00000000000..2e6ecf4a96c --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/invalid-impid.json @@ -0,0 +1,20 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp1-resp1", + "seatbid": [{"bid": [{"id": "req1-imp1-bid1", + "impid": "invalid", + "price": 0.12, + "adm": "a banner"}]}], + "cur": "USD"}}}], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{"value": "unknown impid: invalid", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/invalid-response.json b/adapters/adoppler/adopplertest/supplemental/invalid-response.json new file mode 100644 index 00000000000..72420881aec --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/invalid-response.json @@ -0,0 +1,15 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 200, + "body": "invalid-json"}}], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{"value": "invalid body: json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/invalid-video-ext.json b/adapters/adoppler/adopplertest/supplemental/invalid-video-ext.json new file mode 100644 index 00000000000..d9cb6daa55d --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/invalid-video-ext.json @@ -0,0 +1,43 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit1"}}}, + {"id": "imp2", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit2"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp1-resp1", + "seatbid": [{"bid": [{"id": "req1-imp1-bid1", + "impid": "imp1", + "price": 0.24, + "adm": "", + "cat": ["IAB1", "IAB2"], + "ext": {}}]}], + "cur": "USD"}}}, + {"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit2", + "body": {"id": "req1-unit2", + "imp": [{"id": "imp2", + "video": {"minduration": 120, + "mimes": ["video/mp4"]}, + "ext": {"bidder": {"adunit": "unit2"}}}]}}, + "mockResponse": {"status": 200, + "body": {"id": "req1-imp2-resp2", + "seatbid": [{"bid": [{"id": "req1-imp2-bid2", + "impid": "imp2", + "price": 0.24, + "adm": "", + "cat": ["IAB1", "IAB2"], + "ext": ""}]}], + "cur": "USD"}}}], + "expectedMakeBidsErrors": [{"value": "$.seatbid.bid.ext.ads.video required", + "comparison": "literal"}, + {"value": "json: cannot unmarshal string into Go value of type struct { Ads *adoppler.adsImpExt \"json:\\\"ads\\\"\" }", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/missing-adunit.json b/adapters/adoppler/adopplertest/supplemental/missing-adunit.json new file mode 100644 index 00000000000..82a6a95ed58 --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/missing-adunit.json @@ -0,0 +1,9 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {}}}]}, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [{"value": "$.imp.ext.adoppler.adunit required", + "comparison": "literal"}]} diff --git a/adapters/adoppler/adopplertest/supplemental/server-error.json b/adapters/adoppler/adopplertest/supplemental/server-error.json new file mode 100644 index 00000000000..df23bac07df --- /dev/null +++ b/adapters/adoppler/adopplertest/supplemental/server-error.json @@ -0,0 +1,15 @@ +{"mockBidRequest": {"id": "req1", + "imp":[{"id": "imp1", + "banner": {"w": 100, + "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}, + "httpCalls": [{"expectedRequest": {"uri": "http://adoppler.com/processHeaderBid/unit1", + "body": {"id": "req1-unit1", + "imp": [{"id": "imp1", + "banner": {"w": 100, "h": 200}, + "ext": {"bidder": {"adunit": "unit1"}}}]}}, + "mockResponse": {"status": 500, + "body": ""}}], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{"value": "unexpected status: 500", + "comparison": "literal"}]} diff --git a/config/config.go b/config/config.go index 943d18a95de..52686422039 100644 --- a/config/config.go +++ b/config/config.go @@ -673,6 +673,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx") v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}") + v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 95f5b7f5882..d169c1204bf 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -12,6 +12,7 @@ import ( "github.com/prebid/prebid-server/adapters/adform" "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" + "github.com/prebid/prebid-server/adapters/adoppler" "github.com/prebid/prebid-server/adapters/adpone" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" @@ -71,6 +72,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdform: adform.NewAdformBidder(client, cfg.Adapters[string(openrtb_ext.BidderAdform)].Endpoint), openrtb_ext.BidderAdkernel: adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint), openrtb_ext.BidderAdkernelAdn: adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint), + openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), openrtb_ext.BidderAdpone: adpone.NewAdponeBidder(cfg.Adapters[string(openrtb_ext.BidderAdpone)].Endpoint), openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), openrtb_ext.BidderAdvangelists: advangelists.NewAdvangelistsBidder(cfg.Adapters[string(openrtb_ext.BidderAdvangelists)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 7a3f24eb07f..6e70ef4b6fa 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -29,6 +29,7 @@ const ( BidderAdvangelists BidderName = "advangelists" BidderApplogy BidderName = "applogy" BidderAppnexus BidderName = "appnexus" + BidderAdoppler BidderName = "adoppler" BidderBeachfront BidderName = "beachfront" BidderBrightroll BidderName = "brightroll" BidderConsumable BidderName = "consumable" @@ -84,6 +85,7 @@ var BidderMap = map[string]BidderName{ "advangelists": BidderAdvangelists, "applogy": BidderApplogy, "appnexus": BidderAppnexus, + "adoppler": BidderAdoppler, "beachfront": BidderBeachfront, "brightroll": BidderBrightroll, "consumable": BidderConsumable, diff --git a/openrtb_ext/imp_adoppler.go b/openrtb_ext/imp_adoppler.go new file mode 100644 index 00000000000..4b3ba97ce05 --- /dev/null +++ b/openrtb_ext/imp_adoppler.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpAdoppler struct { + AdUnit string `json:"adunit"` +} diff --git a/static/bidder-info/adoppler.yaml b/static/bidder-info/adoppler.yaml new file mode 100644 index 00000000000..7fa79eda163 --- /dev/null +++ b/static/bidder-info/adoppler.yaml @@ -0,0 +1,11 @@ +maintainer: + email: info@adoppler.com +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/adoppler.json b/static/bidder-params/adoppler.json new file mode 100644 index 00000000000..c2bdde4f60f --- /dev/null +++ b/static/bidder-params/adoppler.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adoppler Adapter Params", + "description": "A schema which validates params accepted by the Adoppler adapter", + "type": "object", + "properties": { + "adunit": { + "type": "string", + "description": "AdUnit to bid against to." + } + }, + "required": ["adunit"] +} diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index ded8fd2bd78..87a9caebf96 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -65,6 +65,7 @@ func TestNewSyncerMap(t *testing.T) { } adaptersWithoutSyncers := map[openrtb_ext.BidderName]bool{ + openrtb_ext.BidderAdoppler: true, openrtb_ext.BidderApplogy: true, openrtb_ext.BidderTappx: true, openrtb_ext.BidderKubient: true, From fd78c23caef9986556b8558443a7e0cb91831a3a Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Wed, 26 Feb 2020 15:54:37 -0800 Subject: [PATCH 017/318] Adding support for deal prefixes (#1183) --- adapters/appnexus/appnexus.go | 8 +- .../exemplary/simple-auction.json | 6 +- .../video/simple-video.json | 6 +- .../appnexustest/amp/simple-banner.json | 6 +- .../appnexustest/amp/simple-video.json | 6 +- .../appnexustest/exemplary/native-1.1.json | 6 +- .../appnexustest/exemplary/simple-banner.json | 6 +- .../appnexustest/exemplary/simple-video.json | 6 +- .../exemplary/video-invalid-category.json | 6 +- .../supplemental/displaymanager-test.json | 6 +- .../appnexustest/supplemental/multi-bid.json | 12 +- adapters/bidder.go | 8 +- endpoints/openrtb2/video_auction.go | 5 +- exchange/bidder.go | 17 +- exchange/bidder_test.go | 9 +- exchange/exchange.go | 82 +++++ exchange/exchange_test.go | 296 ++++++++++++++++-- openrtb_ext/bid_request_video.go | 8 + openrtb_ext/request.go | 1 + 19 files changed, 442 insertions(+), 58 deletions(-) diff --git a/adapters/appnexus/appnexus.go b/adapters/appnexus/appnexus.go index 3986bfd45b0..9bec9bf1e3b 100644 --- a/adapters/appnexus/appnexus.go +++ b/adapters/appnexus/appnexus.go @@ -87,6 +87,7 @@ type appnexusBidExtAppnexus struct { BrandId int `json:"brand_id"` BrandCategory int `json:"brand_category_id"` CreativeInfo appnexusBidExtCreative `json:"creative_info"` + DealPriority int `json:"deal_priority"` } type appnexusBidExt struct { @@ -543,9 +544,10 @@ func (a *AppNexusAdapter) MakeBids(internalRequest *openrtb.BidRequest, external } bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: bidType, - BidVideo: impVideo, + Bid: &bid, + BidType: bidType, + BidVideo: impVideo, + DealPriority: bidExt.Appnexus.DealPriority, }) } else { errs = append(errs, err) diff --git a/adapters/appnexus/appnexusplatformtest/exemplary/simple-auction.json b/adapters/appnexus/appnexusplatformtest/exemplary/simple-auction.json index 03c3f4c5880..e0c0435faab 100644 --- a/adapters/appnexus/appnexusplatformtest/exemplary/simple-auction.json +++ b/adapters/appnexus/appnexusplatformtest/exemplary/simple-auction.json @@ -80,7 +80,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -118,7 +119,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexusplatformtest/video/simple-video.json b/adapters/appnexus/appnexusplatformtest/video/simple-video.json index 85960427d81..7ee192be2c1 100644 --- a/adapters/appnexus/appnexusplatformtest/video/simple-video.json +++ b/adapters/appnexus/appnexusplatformtest/video/simple-video.json @@ -80,7 +80,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -118,7 +119,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/amp/simple-banner.json b/adapters/appnexus/appnexustest/amp/simple-banner.json index 646359b4267..54e6a143e19 100644 --- a/adapters/appnexus/appnexustest/amp/simple-banner.json +++ b/adapters/appnexus/appnexustest/amp/simple-banner.json @@ -91,7 +91,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -129,7 +130,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/amp/simple-video.json b/adapters/appnexus/appnexustest/amp/simple-video.json index a6f96be34b8..061d5c94369 100644 --- a/adapters/appnexus/appnexustest/amp/simple-video.json +++ b/adapters/appnexus/appnexustest/amp/simple-video.json @@ -82,7 +82,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -120,7 +121,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/exemplary/native-1.1.json b/adapters/appnexus/appnexustest/exemplary/native-1.1.json index 86b75505e0c..189304fdb4c 100644 --- a/adapters/appnexus/appnexustest/exemplary/native-1.1.json +++ b/adapters/appnexus/appnexustest/exemplary/native-1.1.json @@ -96,7 +96,8 @@ "brand_category_id": 350, "auction_id": 5607483846416358664, "bidder_id": 2, - "bid_ad_type": 3 + "bid_ad_type": 3, + "deal_priority": 5 } } } @@ -136,7 +137,8 @@ "brand_category_id": 350, "auction_id": 5607483846416358664, "bidder_id": 2, - "bid_ad_type": 3 + "bid_ad_type": 3, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/exemplary/simple-banner.json b/adapters/appnexus/appnexustest/exemplary/simple-banner.json index e5bd311648f..59931fb6ad7 100644 --- a/adapters/appnexus/appnexustest/exemplary/simple-banner.json +++ b/adapters/appnexus/appnexustest/exemplary/simple-banner.json @@ -89,7 +89,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -127,7 +128,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/exemplary/simple-video.json b/adapters/appnexus/appnexustest/exemplary/simple-video.json index 15755c7de37..ced90c39549 100644 --- a/adapters/appnexus/appnexustest/exemplary/simple-video.json +++ b/adapters/appnexus/appnexustest/exemplary/simple-video.json @@ -80,7 +80,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -118,7 +119,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/exemplary/video-invalid-category.json b/adapters/appnexus/appnexustest/exemplary/video-invalid-category.json index d3686af00a9..257905c873f 100644 --- a/adapters/appnexus/appnexustest/exemplary/video-invalid-category.json +++ b/adapters/appnexus/appnexustest/exemplary/video-invalid-category.json @@ -79,7 +79,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -116,7 +117,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 1, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/supplemental/displaymanager-test.json b/adapters/appnexus/appnexustest/supplemental/displaymanager-test.json index d5c981c6945..c6ad330e3a8 100644 --- a/adapters/appnexus/appnexustest/supplemental/displaymanager-test.json +++ b/adapters/appnexus/appnexustest/supplemental/displaymanager-test.json @@ -106,7 +106,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -144,7 +145,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/appnexus/appnexustest/supplemental/multi-bid.json b/adapters/appnexus/appnexustest/supplemental/multi-bid.json index 7234551ea3f..9e63bdced95 100644 --- a/adapters/appnexus/appnexustest/supplemental/multi-bid.json +++ b/adapters/appnexus/appnexustest/supplemental/multi-bid.json @@ -89,7 +89,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 4 } } }, @@ -112,7 +113,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }] @@ -150,7 +152,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 4 } } }, @@ -177,7 +180,8 @@ "auction_id": 8189378542222915032, "bid_ad_type": 0, "bidder_id": 2, - "ranking_price": 0.000000 + "ranking_price": 0.000000, + "deal_priority": 5 } } }, diff --git a/adapters/bidder.go b/adapters/bidder.go index baec4135b6a..627caf67344 100644 --- a/adapters/bidder.go +++ b/adapters/bidder.go @@ -108,10 +108,12 @@ func NewBidderResponse() *BidderResponse { // TypedBid.Bid.Ext will become "response.seatbid[i].bid.ext.bidder" in the final OpenRTB response. // TypedBid.BidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response. // TypedBid.BidVideo will become "response.seatbid[i].bid.ext.prebid.video" in the final OpenRTB response. +// TypedBid.DealPriority will become "response.seatbid[i].bid.dealPriority" in the final OpenRTB response. type TypedBid struct { - Bid *openrtb.Bid - BidType openrtb_ext.BidType - BidVideo *openrtb_ext.ExtBidPrebidVideo + Bid *openrtb.Bid + BidType openrtb_ext.BidType + BidVideo *openrtb_ext.ExtBidPrebidVideo + DealPriority int } // RequestData and ResponseData exist so that prebid-server core code can implement its "debug" functionality diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index b8b21b762d7..7c9651af747 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -550,8 +550,9 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro } prebid := openrtb_ext.ExtRequestPrebid{ - Cache: &cache, - Targeting: &targeting, + Cache: &cache, + Targeting: &targeting, + SupportDeals: videoRequest.SupportDeals, } extReq := openrtb_ext.ExtRequest{Prebid: prebid} diff --git a/exchange/bidder.go b/exchange/bidder.go index d9a28fee175..97f64e74bb5 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -51,11 +51,13 @@ type adaptedBidder interface { // pbsOrtbBid.bidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response. // pbsOrtbBid.bidTargets does not need to be filled out by the Bidder. It will be set later by the exchange. // pbsOrtbBid.bidVideo is optional but should be filled out by the Bidder if bidType is video. +// pbsOrtbBid.dealPriority will become "response.seatbid[i].bid.dealPriority" in the final OpenRTB response. type pbsOrtbBid struct { - bid *openrtb.Bid - bidType openrtb_ext.BidType - bidTargets map[string]string - bidVideo *openrtb_ext.ExtBidPrebidVideo + bid *openrtb.Bid + bidType openrtb_ext.BidType + bidTargets map[string]string + bidVideo *openrtb_ext.ExtBidPrebidVideo + dealPriority int } // pbsOrtbSeatBid is a SeatBid returned by an adaptedBidder. @@ -183,9 +185,10 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi bidResponse.Bids[i].Bid.Price = bidResponse.Bids[i].Bid.Price * bidAdjustment * conversionRate } seatBid.bids = append(seatBid.bids, &pbsOrtbBid{ - bid: bidResponse.Bids[i].Bid, - bidType: bidResponse.Bids[i].BidType, - bidVideo: bidResponse.Bids[i].BidVideo, + bid: bidResponse.Bids[i].Bid, + bidType: bidResponse.Bids[i].BidType, + bidVideo: bidResponse.Bids[i].BidVideo, + dealPriority: bidResponse.Bids[i].DealPriority, }) } } else { diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 173bc37ee51..46f63cc66c4 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -39,13 +39,15 @@ func TestSingleBidder(t *testing.T) { Bid: &openrtb.Bid{ Price: firstInitialPrice, }, - BidType: openrtb_ext.BidTypeBanner, + BidType: openrtb_ext.BidTypeBanner, + DealPriority: 4, }, { Bid: &openrtb.Bid{ Price: secondInitialPrice, }, - BidType: openrtb_ext.BidTypeVideo, + BidType: openrtb_ext.BidTypeVideo, + DealPriority: 5, }, }, } @@ -88,6 +90,9 @@ func TestSingleBidder(t *testing.T) { if typedBid.BidType != seatBid.bids[index].bidType { t.Errorf("Bid %d did not have the right type. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) } + if typedBid.DealPriority != seatBid.bids[index].dealPriority { + t.Errorf("Bid %d did not have the right deal priority. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) + } } if mockBidderResponse.Bids[0].Bid.Price != bidAdjustment*firstInitialPrice { t.Errorf("Bid[0].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*firstInitialPrice, mockBidderResponse.Bids[0].Bid.Price) diff --git a/exchange/exchange.go b/exchange/exchange.go index 8c6cac4cfcd..ef10180a745 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -10,6 +10,7 @@ import ( "net/http" "runtime/debug" "sort" + "strings" "time" "github.com/prebid/prebid-server/stored_requests" @@ -167,12 +168,93 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } targData.setTargeting(auc, bidRequest.App != nil, bidCategory) } + + if requestExt.Prebid.SupportDeals { + dealErrs := applyDealSupport(bidRequest, auc) + errs = append(errs, dealErrs...) + } } // Build the response return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, errs) } +type DealTierInfo struct { + Prefix string `json:"prefix"` + MinDealTier int `json:"minDealTier"` +} + +type DealTier struct { + Info *DealTierInfo `json:"dealTier,omitempty"` +} + +type BidderDealTier struct { + DealInfo map[string]*DealTier +} + +// applyDealSupport updates targeting keys with deal prefixes if minimum deal tier exceeded +func applyDealSupport(bidRequest *openrtb.BidRequest, auc *auction) []error { + errs := []error{} + impDealMap := getDealTiers(bidRequest) + + for impID, topBidsPerImp := range auc.winningBidsByBidder { + impDeal := impDealMap[impID].DealInfo + for bidder, topBidPerBidder := range topBidsPerImp { + bidderString := bidder.String() + + if topBidPerBidder.dealPriority > 0 { + if validateAndNormalizeDealTier(impDeal[bidderString]) { + updateHbPbCatDur(topBidPerBidder, impDeal[bidderString].Info) + } else { + errs = append(errs, fmt.Errorf("dealTier configuration invalid for bidder '%s', imp ID '%s'", bidderString, impID)) + } + } + } + } + + return errs +} + +// getDealTiers creates map of impression to bidder deal tier configuration +func getDealTiers(bidRequest *openrtb.BidRequest) map[string]*BidderDealTier { + impDealMap := make(map[string]*BidderDealTier) + + for _, imp := range bidRequest.Imp { + var bidderDealTier BidderDealTier + err := json.Unmarshal(imp.Ext, &bidderDealTier.DealInfo) + if err != nil { + continue + } + + impDealMap[imp.ID] = &bidderDealTier + } + + return impDealMap +} + +func validateAndNormalizeDealTier(impDeal *DealTier) bool { + if impDeal == nil || impDeal.Info == nil { + return false + } + // Remove whitespace from prefix before checking if it can be used + impDeal.Info.Prefix = strings.ReplaceAll(impDeal.Info.Prefix, " ", "") + return len(impDeal.Info.Prefix) > 0 && impDeal.Info.MinDealTier > 0 +} + +func updateHbPbCatDur(bid *pbsOrtbBid, dealTierInfo *DealTierInfo) { + if bid.dealPriority >= dealTierInfo.MinDealTier { + prefixTier := fmt.Sprintf("%s%d_", dealTierInfo.Prefix, bid.dealPriority) + + if oldCatDur, ok := bid.bidTargets["hb_pb_cat_dur"]; ok { + oldCatDurSplit := strings.SplitAfterN(oldCatDur, "_", 2) + oldCatDurSplit[0] = prefixTier + + newCatDur := strings.Join(oldCatDurSplit, "") + bid.bidTargets["hb_pb_cat_dur"] = newCatDur + } + } +} + func (e *exchange) makeAuctionContext(ctx context.Context, needsCache bool) (auctionCtx context.Context, cancel context.CancelFunc) { auctionCtx = ctx cancel = func() {} diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 7e199d4b750..0a64bce0826 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -914,10 +914,10 @@ func TestCategoryMapping(t *testing.T) { bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1} - bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}} - bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}} - bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, 0} + bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} innerBids := []*pbsOrtbBid{ &bid1_1, @@ -969,10 +969,10 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1} - bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}} - bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}} - bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}} + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, 0} + bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, 0} innerBids := []*pbsOrtbBid{ &bid1_1, @@ -1023,9 +1023,9 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1} bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} - bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}} - bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} innerBids := []*pbsOrtbBid{ &bid1_1, @@ -1105,9 +1105,9 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1} bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} - bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}} - bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} innerBids := []*pbsOrtbBid{ &bid1_1, @@ -1156,10 +1156,10 @@ func TestCategoryDedupe(t *testing.T) { bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 10.0000, Cat: cats1, W: 1, H: 1} bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1} - bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}} - bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} - bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}} + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} selectedBids := make(map[string]int) expectedCategories := map[string]string{ @@ -1288,7 +1288,7 @@ func TestBidRejectionErrors(t *testing.T) { innerBids := []*pbsOrtbBid{} for _, bid := range test.bids { currentBid := pbsOrtbBid{ - bid, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, + bid, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, 0, } innerBids = append(innerBids, ¤tBid) } @@ -1325,6 +1325,264 @@ func TestUpdateRejections(t *testing.T) { assert.Containsf(t, rejections, "bid rejected [bid ID: bid_id2] reason: some reason 2", "Rejection message did not match expected") } +func TestApplyDealSupport(t *testing.T) { + testCases := []struct { + description string + dealPriority int + impExt json.RawMessage + targ map[string]string + expectedHbPbCatDur string + expectedDealErr string + }{ + { + description: "hb_pb_cat_dur should be modified", + dealPriority: 5, + impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_movies_30s", + }, + expectedHbPbCatDur: "tier5_movies_30s", + expectedDealErr: "", + }, + { + description: "hb_pb_cat_dur should not be modified due to priority not exceeding min", + dealPriority: 9, + impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 10, "prefix": "tier"}, "placementId": 10433394}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_medicine_30s", + }, + expectedHbPbCatDur: "12.00_medicine_30s", + expectedDealErr: "", + }, + { + description: "hb_pb_cat_dur should not be modified due to invalid config", + dealPriority: 5, + impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_games_30s", + }, + expectedHbPbCatDur: "12.00_games_30s", + expectedDealErr: "dealTier configuration invalid for bidder 'appnexus', imp ID 'imp_id1'", + }, + { + description: "hb_pb_cat_dur should not be modified due to deal priority of 0", + dealPriority: 0, + impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_auto_30s", + }, + expectedHbPbCatDur: "12.00_auto_30s", + expectedDealErr: "", + }, + } + + bidderName := openrtb_ext.BidderName("appnexus") + for _, test := range testCases { + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{ + { + ID: "imp_id1", + Ext: test.impExt, + }, + }, + } + + bid := pbsOrtbBid{&openrtb.Bid{}, "video", test.targ, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + + auc := &auction{ + winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ + "imp_id1": { + bidderName: &bid, + }, + }, + } + + dealErrs := applyDealSupport(bidRequest, auc) + + assert.Equal(t, test.expectedHbPbCatDur, auc.winningBidsByBidder["imp_id1"][bidderName].bidTargets["hb_pb_cat_dur"], test.description) + if len(test.expectedDealErr) > 0 { + assert.Containsf(t, dealErrs, errors.New(test.expectedDealErr), "Expected error message not found in deal errors") + } + } +} + +func TestGetDealTiers(t *testing.T) { + testCases := []struct { + impExt json.RawMessage + bidderResult map[string]bool // true indicates bidder had valid config, false indicates invalid + }{ + { + impExt: json.RawMessage(`{"validbase": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + bidderResult: map[string]bool{ + "validbase": true, + }, + }, + { + impExt: json.RawMessage(`{"validmultiple1": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}, "validmultiple2": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + bidderResult: map[string]bool{ + "validmultiple1": true, + "validmultiple2": true, + }, + }, + { + impExt: json.RawMessage(`{"nodealtier": {"placementId": 10433394}}`), + bidderResult: map[string]bool{ + "nodealtier": false, + }, + }, + { + impExt: json.RawMessage(`{"validbase": {"placementId": 10433394}, "onedealTier2": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + bidderResult: map[string]bool{ + "onedealTier2": true, + "validbase": false, + }, + }, + } + + filledDealTier := DealTier{ + Info: &DealTierInfo{ + Prefix: "tier", + MinDealTier: 5, + }, + } + emptyDealTier := DealTier{} + + for _, test := range testCases { + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{ + { + ID: "imp_id1", + Ext: test.impExt, + }, + }, + } + + impDealMap := getDealTiers(bidRequest) + + for bidder, valid := range test.bidderResult { + if valid { + assert.Equal(t, &filledDealTier, impDealMap["imp_id1"].DealInfo[bidder], "DealTier should be filled with config data") + } else { + assert.Equal(t, &emptyDealTier, impDealMap["imp_id1"].DealInfo[bidder], "DealTier should be empty") + } + } + } +} + +func TestValidateAndNormalizeDealTier(t *testing.T) { + testCases := []struct { + description string + params json.RawMessage + expectedResult bool + }{ + { + description: "BidderDealTier should be valid", + params: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + expectedResult: true, + }, + { + description: "BidderDealTier should be invalid due to empty prefix", + params: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}`), + expectedResult: false, + }, + { + description: "BidderDealTier should be invalid due to empty dealTier", + params: json.RawMessage(`{"appnexus": {"dealTier": {}, "placementId": 10433394}}`), + expectedResult: false, + }, + { + description: "BidderDealTier should be invalid due to missing minDealTier", + params: json.RawMessage(`{"appnexus": {"dealTier": {"prefix": "tier"}, "placementId": 10433394}}`), + expectedResult: false, + }, + { + description: "BidderDealTier should be invalid due to missing dealTier", + params: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), + expectedResult: false, + }, + { + description: "BidderDealTier should be invalid due to prefix containing all whitespace", + params: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": " "}, "placementId": 10433394}}`), + expectedResult: false, + }, + { + description: "BidderDealTier should be valid after removing whitespace", + params: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": " prefixwith sp aces "}, "placementId": 10433394}}`), + expectedResult: true, + }, + } + + for _, test := range testCases { + var bidderDealTier BidderDealTier + err := json.Unmarshal(test.params, &bidderDealTier.DealInfo) + if err != nil { + assert.Fail(t, "Unable to unmarshal JSON data for testing BidderDealTier") + } + + assert.Equal(t, test.expectedResult, validateAndNormalizeDealTier(bidderDealTier.DealInfo["appnexus"]), test.description) + } +} + +func TestUpdateHbPbCatDur(t *testing.T) { + testCases := []struct { + description string + targ map[string]string + dealTier *DealTierInfo + dealPriority int + expectedHbPbCatDur string + }{ + { + description: "hb_pb_cat_dur should be updated with prefix and tier", + targ: map[string]string{ + "hb_pb": "12.00", + "hb_pb_cat_dur": "12.00_movies_30s", + }, + dealTier: &DealTierInfo{ + Prefix: "tier", + MinDealTier: 5, + }, + dealPriority: 5, + expectedHbPbCatDur: "tier5_movies_30s", + }, + { + description: "hb_pb_cat_dur should not be updated due to bid priority", + targ: map[string]string{ + "hb_pb": "12.00", + "hb_pb_cat_dur": "12.00_auto_30s", + }, + dealTier: &DealTierInfo{ + Prefix: "tier", + MinDealTier: 10, + }, + dealPriority: 6, + expectedHbPbCatDur: "12.00_auto_30s", + }, + { + description: "hb_pb_cat_dur should be updated with prefix and tier", + targ: map[string]string{ + "hb_pb": "12.00", + "hb_pb_cat_dur": "12.00_medicine_30s", + }, + dealTier: &DealTierInfo{ + Prefix: "tier", + MinDealTier: 1, + }, + dealPriority: 7, + expectedHbPbCatDur: "tier7_medicine_30s", + }, + } + + for _, test := range testCases { + bid := pbsOrtbBid{&openrtb.Bid{}, "video", test.targ, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + + updateHbPbCatDur(&bid, test.dealTier) + + assert.Equal(t, test.expectedHbPbCatDur, bid.bidTargets["hb_pb_cat_dur"], test.description) + } +} + type exchangeSpec struct { IncomingRequest exchangeRequest `json:"incomingRequest"` OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go index 454476857a4..f7ddf203294 100644 --- a/openrtb_ext/bid_request_video.go +++ b/openrtb_ext/bid_request_video.go @@ -136,6 +136,14 @@ type BidRequestVideo struct { // Description: // Contains the OpenRTB Regs object to be passed to OpenRTB request Regs *openrtb.Regs `json:"regs,omitempty"` + + // Attribute: + // supportdeals + // Type: + // bool; optional + // Description: + // Indicates that the response should update key to include prefix and tier + SupportDeals bool `json:"supportdeals,omitempty"` } type PodConfig struct { diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 9226ff294d5..ee1a0cd0f8b 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -17,6 +17,7 @@ type ExtRequestPrebid struct { Cache *ExtRequestPrebidCache `json:"cache,omitempty"` StoredRequest *ExtStoredRequest `json:"storedrequest,omitempty"` Targeting *ExtRequestTargeting `json:"targeting,omitempty"` + SupportDeals bool `json:"supportdeals,omitempty"` } // ExtRequestPrebidCache defines the contract for bidrequest.ext.prebid.cache From 7be0a4d68832679d71a0a11f1ff01420866d40b9 Mon Sep 17 00:00:00 2001 From: PubMatic-OpenWrap Date: Fri, 28 Feb 2020 00:25:22 +0530 Subject: [PATCH 018/318] updating default hard-coded list of certs (#1201) Co-authored-by: Shalmali Patil --- ssl/ssl.go | 3230 ++++++++++++++++++++-------------------------------- 1 file changed, 1206 insertions(+), 2024 deletions(-) diff --git a/ssl/ssl.go b/ssl/ssl.go index d05c90154b9..a424cd9f54b 100644 --- a/ssl/ssl.go +++ b/ssl/ssl.go @@ -40,29 +40,6 @@ func AppendPEMFileToRootCAPool(certPool *x509.CertPool, pemFileName string) (*x5 var pemCerts = []byte(` -----BEGIN CERTIFICATE----- -MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB -VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp -bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R -dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw -MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy -dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52 -ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM -EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj -lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ -znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH -2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1 -k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs -2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD -VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC -AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG -KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+ -8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R -FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS -mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE -DNuxUCAKGkq6ahq97BvIxYSazQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ @@ -107,74 +84,36 @@ d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE -AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x -CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW -MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF -RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC -AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7 -09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7 -XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P -Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK -t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb -X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28 -MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU -fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI -2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH -K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae -ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP -BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ -MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw -RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv -bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm -fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3 -gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe -I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i -5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi -ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn -MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ -o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6 -zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN -GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt -r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK -Z05phkOTOPu220+DkdRgfks+KzgHVZhepA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx -CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp -ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa -QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw -NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft -ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu -QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG -qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL -fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ -Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4 -Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ -54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b -MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j -ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej -YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt -A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF -rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ -pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB -lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy -YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50 -7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs -YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6 -xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc -unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/ -Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp -ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42 -gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0 -jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+ -XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD -W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/ -RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r -MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk -BYn8eNZcLCZDqQ== +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE @@ -235,79 +174,6 @@ c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw -MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD -VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul -CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n -tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl -dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch -PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC -+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O -BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk -ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB -IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X -7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz -43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY -eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl -pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA -WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx -MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB -ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV -BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV -6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX -GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP -dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH -1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF -62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW -BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw -AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL -MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU -cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv -b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6 -IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/ -iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao -GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh -4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm -XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 -MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK -EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh -BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq -xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G -87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i -2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U -WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 -0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G -A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr -pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL -ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm -aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv -hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm -hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X -dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 -P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y -iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no -xqE= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL @@ -392,81 +258,80 @@ aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP -bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 -MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft -ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk -hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym -1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW -OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb -2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko -O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU -AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB -BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF -Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb -LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir -oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C -MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds -sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP -bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2 -MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft -ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC -206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci -KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2 -JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9 -BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e -Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B -PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67 -Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq -Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ -o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3 -+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj -YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj -FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn -xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2 -LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc -obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8 -CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe -IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA -DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F -AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX -Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb -AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl -Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw -RY8mkaKO/qk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc -MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp -b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT -AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs -aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H -j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K -f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55 -IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw -FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht -QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm -/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ -k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ -MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC -seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ -hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+ -eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U -DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj -B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL -rosot4LKGAfmt1t06SAZf7IbiVQ= +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE @@ -546,26 +411,6 @@ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg -Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL -MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD -VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0 -ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX -l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB -HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B -5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3 -WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD -AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP -gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+ -DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu -BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs -h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk -LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow @@ -597,26 +442,6 @@ I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg -Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL -MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD -VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg -isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z -NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI -+MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R -hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+ -mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD -AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP -Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s -EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2 -mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC -e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow -dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow @@ -648,61 +473,6 @@ u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq 4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET -MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE -AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw -CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg -YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE -Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX -mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD -XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW -S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp -FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD -AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu -ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z -ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv -Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw -DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6 -yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq -EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ -CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB -EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN -PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV -BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu -MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy -MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx -EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk -D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o -OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A -fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe -IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n -oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK -/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj -rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD -3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE -7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC -yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd -qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI -hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA -SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo -HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB -emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC -AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb -7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x -DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk -F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF -a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT -Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy @@ -734,24 +504,36 @@ zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD -TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2 -MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF -Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh -IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6 -dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO -V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC -GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN -v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB -AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB -Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO -76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK -OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH -ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi -yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL -buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj -2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE= +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB @@ -795,60 +577,38 @@ fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa -MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB -ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw -IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B -AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb -unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d -BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq -7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 -0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX -roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG -A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j -aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p -26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA -BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud -EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN -BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB -AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd -p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi -1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc -XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 -eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu -tGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo -YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 -MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy -NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G -A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA -A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 -Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s -QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV -eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 -B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh -z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T -AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i -ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w -TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH -MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD -VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE -VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B -AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM -bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi -ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG -VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c -ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ -AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV @@ -873,36 +633,36 @@ t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET -MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk -BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4 -Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl -cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0 -aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY -F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N -8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe -rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K -/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu -7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC -28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6 -lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E -nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB -0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09 -5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj -WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN -jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ -KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s -ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM -OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q -619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn -2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj -o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v -nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG -5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq -pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb -dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0 -BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5 +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw @@ -927,23 +687,49 @@ kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 l7+ijrRU -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E -jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo -ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI -ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu -Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg -AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 -HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA -uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa -TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg -xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q -CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x -O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs -6GAqm4VKQPNriiTsBhYscw== +MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa +MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy +dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a +iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt +6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP +0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f +6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE +EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN +1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc +h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT +mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV +4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO +WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud +DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd +Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq +hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh +66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7 +/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS +S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j +2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R +Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr +RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy +6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV +V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5 +g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl +++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x +CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs +dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat +93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x +Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P +AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj +FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG +SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch +p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal +U5ORGpOucGpnutee5WEaXw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM @@ -968,6 +754,40 @@ VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 @@ -1010,74 +830,6 @@ OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ d0jQ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC -Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g -Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0 -aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa -Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg -SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo -aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp -ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z -7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA// -DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx -zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8 -hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs -4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u -gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY -NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E -FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3 -j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG -52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB -echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws -ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI -zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy -wy39FCqQmbkHzJ8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0 -MRMwEQYDVQQDEwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQG -EwJJTDAeFw0wNDAzMjQxMTMyMThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMT -CkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNpZ24xCzAJBgNVBAYTAklMMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49qROR+WCf4C9DklBKK -8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTyP2Q2 -98CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb -2CEJKHxNGGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxC -ejVb7Us6eva1jsz/D3zkYDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7Kpi -Xd3DTKaCQeQzC6zJMw9kglcq/QytNuEMrkvF7zuZ2SOzW120V+x0cAwqTwIDAQAB -o4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Zl -ZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0PAQH/BAQD -AgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRL -AZs+VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWd -foPPbrxHbvUanlR2QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0M -cXS6hMTXcpuEfDhOZAYnKuGntewImbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq -8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb/627HOkthIDYIb6FUtnUdLlp -hbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VGzT2ouvDzuFYk -Res3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U -AGegcQCCSA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw -PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu -MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx -GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL -MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf -HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh -gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW -v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue -Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr -9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt -6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7 -MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl -Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58 -ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq -hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p -iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC -dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL -kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL -hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz -OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj @@ -1103,56 +855,6 @@ l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp -ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow -fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV -BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM -cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S -HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 -CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk -3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz -6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV -HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud -EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv -Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw -Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww -DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 -5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj -Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI -gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ -aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl -izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 -aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla -MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO -BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD -VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW -fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt -TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL -fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW -1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 -kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G -A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v -ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo -dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu -Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ -HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 -pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS -jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ -xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn -dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE @@ -1225,30 +927,6 @@ xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx -ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w -MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD -VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx -FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu -ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7 -gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH -fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a -ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT -ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF -MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk -c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto -dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt -aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI -hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk -QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/ -h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR -rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2 -9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow @@ -1313,6 +991,43 @@ H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD @@ -1335,6 +1050,43 @@ YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j @@ -1358,64 +1110,36 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep +OkuE6N36B9K -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV -UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL -EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ -BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x -ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg -bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ -j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV -Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG -SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx -JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI -RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw -MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5 -fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i -+DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG -SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN -QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+ -gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV -UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL -EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ -BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x -ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/ -k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso -LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o -TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG -SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx -JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI -RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3 -MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C -TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5 -WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG -SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR -xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL -B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1 -MQswCQYDVQQGEwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxp -Z2kgQS5TLjE8MDoGA1UEAxMzZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZp -a2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3MDEwNDExMzI0OFoXDTE3MDEwNDEx -MzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0cm9uaWsgQmlsZ2kg -R3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9uaWsg -U2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdU -MZTe1RK6UxYC6lhj71vY8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlT -L/jDj/6z/P2douNffb7tC+Bg62nsM+3YjfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H -5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAIJjjcJRFHLfO6IxClv7wC -90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk9Ok0oSy1 -c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoE -VtstxNulMA0GCSqGSIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLP -qk/CaOv/gKlR6D1id4k9CnU58W5dF4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S -/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwqD2fK/A+JYZ1lpTzlvBNbCNvj -/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4Vwpm+Vganf2X -KWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq -fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV @@ -1454,40 +1178,6 @@ y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV -BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt -ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4 -MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg -SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl -a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h -4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk -tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s -tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL -dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4 -c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um -TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z -+kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O -Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW -OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW -fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2 -l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw -FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+ -8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI -6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO -TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME -wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY -Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn -xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q -DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q -Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t -hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4 -7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7 -QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB 8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1 @@ -1568,34 +1258,6 @@ bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er fF6adulZkMV8gzURZVE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW @@ -1623,70 +1285,79 @@ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m 0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV -UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy -dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 -MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx -dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f -BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A -cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC -AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ -MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm -aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw -ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj -IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF -MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA -A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y -7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh -1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT -ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw -MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj -dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l -c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC -UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc -58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ -o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr -aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA -A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA -Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv -8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc -MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT -ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw -MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j -LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ -KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo -RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu -WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw -Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD -AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK -eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM -zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ -WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN -/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD -VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv -bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv -b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV -UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU -cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds -b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH -iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS -r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 -04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r -GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 -3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P -lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT @@ -1709,27 +1380,6 @@ hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW -MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs -IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg -R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A -PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 -Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL -TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL -5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 -S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe -2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE -FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap -EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td -EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv -/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN -A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 -abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF -I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz -4iIprn2DQKi6bA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx @@ -1854,6 +1504,33 @@ OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw @@ -2006,6 +1683,23 @@ LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI 4uJEvlz36hz1 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p @@ -2031,6 +1725,41 @@ Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI l7WdmplNsDz4SgCbZN2fOUvRJ9e4 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG @@ -2051,28 +1780,97 @@ fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi AmvZWg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT -AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ -TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG -9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw -MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM -BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO -MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2 -LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI -s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2 -xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4 -u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b -F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx -Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd -PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV -HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx -NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF -AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ -L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY -YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg -Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a -NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R -0982gaEbeC9xs/FZTEYYKKuF0mBWWg== +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 @@ -2109,76 +1907,37 @@ naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN -AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp -dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw -MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw -CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ -MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB -SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz -ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH -LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP -PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL -2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w -ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC -MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk -AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0 -AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz -AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz -AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f -BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE -FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY -P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi -CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g -kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95 -HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS -na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q -qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z -TbvGRNs2yyqcjg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw -cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy -b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z -ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4 -NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN -TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p -Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u -uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+ -LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA -vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770 -Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx -62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB -AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw -LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP -BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB -AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov -MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5 -ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn -AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT -AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh -ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo -AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa -AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln -bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p -Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP -PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv -Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB -EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu -w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj -cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV -HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI -VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS -BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS -b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS -8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds -ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl -7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a -86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR -hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/ -MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL +BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV +BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw +MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B +LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F +ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem +hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 +EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn +Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 +zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ +96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m +j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g +DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ +8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j +X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH +hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB +KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 +Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL +BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 +BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO +jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 +loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c +qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ +2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ +JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre +zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf +LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ +x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 +oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD @@ -2229,144 +1988,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx -ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 -b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD -EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 -OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G -A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh -Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l -dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG -SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK -gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX -iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc -Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E -BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G -SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu -b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh -bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv -Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln -aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 -IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh -c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph -biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo -ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP -UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj -YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo -dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA -bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 -sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa -n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS -NitjrFgBazMpUIaD8QFI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx -ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 -b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD -EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X -DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw -DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u -c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr -TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN -BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA -OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC -2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW -RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P -AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW -ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 -YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz -b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO -ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB -IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs -b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs -ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s -YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg -a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g -SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 -aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg -YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg -Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY -ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g -pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 -Fp1hBWeAyNDYpQcCNJgEjTME1A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV -MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe -TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 -dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB -KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 -N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC -dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu -MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL -b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD -zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi -3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 -WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY -Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi -NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC -ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 -QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 -YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz -aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu -IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm -ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg -ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs -amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv -IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 -Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 -ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 -YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg -dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs -b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G -CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO -xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP -0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ -QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk -f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK -8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx -ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 -b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD -EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz -aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w -MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G -A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh -Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l -dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh -bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq -eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe -r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5 -3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd -vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l -mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC -wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg -hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0 -TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh -biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg -ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg -dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6 -b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl -c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0 -ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3 -dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu -ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh -bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo -ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3 -Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u -ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA -A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ -MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+ -NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR -VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY -83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3 -macqaJVmlaut74nLYKkGEsaUR+ko ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp @@ -2414,57 +2035,104 @@ Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ /L7fCg0= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1 -dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s -YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz -dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0 -aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh -IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ -KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw -MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy -b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx -KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG -A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u -aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9 -7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74 -BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G -ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9 -JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0 -PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2 -0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH -0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/ -6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m -v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7 -K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev -bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw -MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w -MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD -gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0 -b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh -bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0 -cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp -ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg -ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq -hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD -AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w -MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag -RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t -UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl -cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v -Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG -AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN -AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS -1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB -3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv -Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh -HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm -pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz -sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE -qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb -mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9 -opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H -YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b +wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX +/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0 +77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP +uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx +p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx +Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2 +TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W +G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw +vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY +EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1 +2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw +DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E +PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf +gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS +FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0 +V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P +XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I +i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t +TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91 +09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky +Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ +AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj +1oxx +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA +MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w +ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw +MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU +T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh +/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e +CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6 +1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE +FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS +gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X +G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy +YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH +vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4 +t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/ +gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3 +5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w +DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz +Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0 +nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT +RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT +wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2 +t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa +TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2 +o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU +3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA +iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f +WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM +S1IK +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx +CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U +cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow +QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl +blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm +3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d +oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5 +DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK +BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q +j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx +4nxp5V2a+EEfOzmTk51V6s2N8fvB -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC @@ -2501,6 +2169,37 @@ xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK SnQ2+Q== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV @@ -2534,6 +2233,37 @@ ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y 8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV @@ -2572,141 +2302,156 @@ mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK 4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy -NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD -cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs -2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY -JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE -Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ -n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A -PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6 -MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp -dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX -BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy -MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp -eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg -/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl -wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh -AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2 -PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu -AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR -MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc -HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/ -Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ -f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO -rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch -6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3 -7CAFYd4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF -UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ -R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN -MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G -A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw -JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+ -WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj -SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl -u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy -A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk -Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7 -MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr -aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC -IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A -cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA -YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA -bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA -bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA -aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA -aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA -ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA -YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA -ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA -LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6 -Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y -eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw -CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G -A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu -Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn -lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt -b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg -9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF -ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC -IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCB -rjELMAkGA1UEBhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcp -MRIwEAYDVQQHEwlTdHV0dGdhcnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fz -c2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVTLVRSVVNUIEF1dGhlbnRpY2F0aW9u -IGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0wNTA2MjIwMDAwMDBa -Fw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFkZW4t -V3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMg -RGV1dHNjaGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJV -U1QgQXV0aGVudGljYXRpb24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBO -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1 -toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob4QSwI7+Vio5bG0F/WsPo -TUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXLg3KSwlOy -ggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1 -XgqfeN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteF -hy+S8dF2g08LOlk3KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm -7QIDAQABo4GSMIGPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEG -MCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJvbmxpbmUxLTIwNDgtNTAdBgNV -HQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAUD8oeXHngovMp -ttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD -pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFo -LtU96G7m1R08P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersF -iXOMy6ZNwPv2AtawB6MDwidAnwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0y -h9WUUpY6RsZxlj33mA6ykaqP2vROJAA5VeitF7nTNCtKqUDMFypVZUF0Qn71wK/I -k63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8bHz2eBIPdltkdOpQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGGTCCBAGgAwIBAgIIPtVRGeZNzn4wDQYJKoZIhvcNAQELBQAwajEhMB8GA1UE -AxMYU0cgVFJVU1QgU0VSVklDRVMgUkFDSU5FMRwwGgYDVQQLExMwMDAyIDQzNTI1 -Mjg5NTAwMDIyMRowGAYDVQQKExFTRyBUUlVTVCBTRVJWSUNFUzELMAkGA1UEBhMC -RlIwHhcNMTAwOTA2MTI1MzQyWhcNMzAwOTA1MTI1MzQyWjBqMSEwHwYDVQQDExhT -RyBUUlVTVCBTRVJWSUNFUyBSQUNJTkUxHDAaBgNVBAsTEzAwMDIgNDM1MjUyODk1 -MDAwMjIxGjAYBgNVBAoTEVNHIFRSVVNUIFNFUlZJQ0VTMQswCQYDVQQGEwJGUjCC -AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqoVgLsfJXwTukK0rcHoyKL -ULO5Lhk9V9sZqtIr5M5C4myh5F0lHjMdtkXRtPpZilZwyW0IdmlwmubHnAgwE/7m -0ZJoYT5MEfJu8rF7V1ZLCb3cD9lxDOiaN94iEByZXtaxFwfTpDktwhpz/cpLKQfC -eSnIyCauLMT8I8hL4oZWDyj9tocbaF85ZEX9aINsdSQePHWZYfrSFPipS7HYfad4 -0hNiZbXWvn5qA7y1svxkMMPQwpk9maTTzdGxxFOHe0wTE2Z/v9VlU2j5XB7ltP82 -mUWjn2LAfxGCAVTeD2WlOa6dSEyJoxA74OaD9bDaLB56HFwfAKzMq6dgZLPGxXvH -VUZ0PJCBDkqOWZ1UsEixUkw7mO6r2jS3U81J2i/rlb4MVxH2lkwEeVyZ1eXkvm/q -R+5RS+8iJq612BGqQ7t4vwt+tN3PdB0lqYljseI0gcSINTjiAg0PE8nVKoIV8IrE -QzJW5FMdHay2z32bll0eZOl0c8RW5BZKUm2SOdPhTQ4/YrnerbUdZbldUv5dCamc -tKQM2S9FdqXPjmqanqqwEaHrYcbrPx78ZrQSnUZ/MhaJvnFFr5Eh2f2Tv7QCkUL/ -SR/tixVo3R+OrJvdggWcRGkWZBdWX0EPSk8ED2VQhpOX7EW/XcIc3M/E2DrmeAXQ -xVVVqV7+qzohu+VyFPcLAgMBAAGjgcIwgb8wHQYDVR0OBBYEFCkgy/HDD9oGjhOT -h/5fYBopu/O2MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUKSDL8cMP2gaO -E5OH/l9gGim787YwEQYDVR0gBAowCDAGBgRVHSAAMEkGA1UdHwRCMEAwPqA8oDqG -OGh0dHA6Ly9jcmwuc2d0cnVzdHNlcnZpY2VzLmNvbS9yYWNpbmUtR3JvdXBlU0cv -TGF0ZXN0Q1JMMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEATEZn -4ERQ9cW2urJRCiUTHbfHiC4fuStkoMuTiFJZqmD1zClSF/8E5ze0MRFGfisebKeL -PEeaXvSqXZA7RT2fSsmKe47A7j55i5KjyJRKuCgRa6YlX129x8j7g09VMeZc8BN8 -471/Kiw3N5RJr4QfFCeiWBCPCjk3GhIgQY8Z9qkfGe2yNLKtfTNEi18KB0PydkVF -La3kjQ4A/QQIqudr+xe9sAhWDjUqcvCz5006Tw3c82ASszhkjNv54SaNL+9O6CRH -PjY0imkPKGuLh8a9hSb50+tpIVZgkdb34GLCqHGuLt5mI7VSRqakSDcsfwEWVxH3 -Jw0O5Q/WkEXhHj8h3NL8FhgTPk1qsiZqQF4leP049KxYejcbmEAEx47J1MRnYbGY -rvDNDty5r2WDewoEij9hqvddQYbmxkzCTzpcVuooO6dEz8hKZPVyYC3jQ7hK4HU8 -MuSqFtcRucFF2ZtmY2blIrc07rrVdC8lZPOBVMt33lfUk+OsBzE6PlwDg1dTx/D+ -aNglUE0SyObhlY1nqzyTPxcCujjXnvcwpT09RAEzGpqfjtCf8e4wiHPvriQZupdz -FcHscQyEZLV77LxpPqRtCRY2yko5isune8YdfucziMm+MG2chZUh6Uc7Bn6B4upG -5nBYgOao8p0LadEziVkw82TTC/bOKwn7fRB2LhA= +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr @@ -2774,27 +2519,6 @@ iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl -MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh -U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz -MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N -IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 -bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE -RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO -zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 -bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF -MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 -VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC -OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW -tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ -q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb -EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ -Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O -VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX @@ -2836,25 +2560,6 @@ JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP -MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx -MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV -BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG -29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk -oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk -3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL -qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN -nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw -DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG -MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX -ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H -DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO -TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv -kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w -zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV @@ -2874,26 +2579,36 @@ Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO -TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy -MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk -ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn -ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71 -9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO -hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U -tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o -BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh -SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww -OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv -cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA -7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k -/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm -eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6 -u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy -7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR -iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO @@ -2929,6 +2644,38 @@ Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw @@ -3000,80 +2747,6 @@ iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn sSi6 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW -MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg -Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 -MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi -U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh -cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk -pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf -OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C -Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT -Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi -HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM -Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w -+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ -Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 -Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B -26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID -AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul -F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC -ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w -ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk -aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 -YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg -c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 -d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG -CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 -dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF -wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS -Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst -0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc -pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl -CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF -P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK -1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm -KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE -JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ -8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm -fyWl8kgAwKQB2j8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW -MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1 -OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG -A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ -JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD -vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo -D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/ -Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW -RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK -HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN -nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM -0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i -UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9 -Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg -TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL -BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K -2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX -UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl -6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK -9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ -HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI -wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY -XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l -IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo -hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr -so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF @@ -3107,39 +2780,6 @@ ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu -IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw -WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD -ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y -IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn -IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ -6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob -jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw -izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl -+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY -zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP -pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF -KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW -ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB -AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 -ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW -IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA -A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 -uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ -FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 -jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ -u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D -YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 -puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa -icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG -DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x -kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z -Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow @@ -3173,108 +2813,6 @@ hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk -MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg -Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT -AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp -Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9 -m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih -FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/ -TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F -EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco -kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu -HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF -vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo -19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC -L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW -bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX -JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw -FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j -BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc -K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf -ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik -Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB -sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e -3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR -ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip -mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH -b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf -rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms -hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y -zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6 -MBr1mmz0DlP5OlvRHA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk -MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg -Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT -AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp -Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr -jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r -0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f -2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP -ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF -y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA -tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL -6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 -uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL -acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh -k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q -VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw -FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O -BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh -b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R -fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv -/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI -REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx -srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv -aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT -woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n -Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W -t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N -8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 -9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 -wSsSnqaeG8XmDtkx2Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw -ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp -dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290 -IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD -VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy -dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg -MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx -UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD -1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH -oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR -HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/ -5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv -idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL -OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC -NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f -46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB -UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth -7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G -A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED -MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB -bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x -XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T -PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0 -Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70 -WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL -Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm -7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S -nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN -vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB -WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI -fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb -I+2ksx0WckNLIOFZfsLorSa/ovc= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl @@ -3321,180 +2859,30 @@ e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p TpPDpFQUWw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf -tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg -uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J -XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK -8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 -5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 -kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS -GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt -ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 -au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV -hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI -dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV -BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 -Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1 -OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i -SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc -VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW -Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q -Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2 -1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq -ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1 -Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX -XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy -dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6 -Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz -JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 -Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN -irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8 -TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6 -g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB -95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj -S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL -MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV -BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 -c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx -MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg -R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD -VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR -JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T -fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu -jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z -wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ -fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD -VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G -CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 -7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn -8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs -ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT -ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ -2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE -SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg -Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV -BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl -cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA -vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu -Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a -0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1 -4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN -eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD -R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG -A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu -dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME -Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3 -WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw -HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ -KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO -Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX -wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ -2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89 -9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0 -jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38 -aQNiuJkFBT1reBK9sG9l ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc -UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg -MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 -dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz -MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy -dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD -VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg -xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu -xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7 -XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k -heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J -YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C -urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1 -JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51 -b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV -9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7 -kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh -fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy -B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA -aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS -RGQDJereW26fyfJOrN3H ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc -UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS -S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg -SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3 -WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv -bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU -UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw -bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe -LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef -J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh -R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ -Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX -JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p -zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S -Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ -KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq -ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 -Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz -gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH -uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS -y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc -UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx -c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS -S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg -SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx -OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry -b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC -VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE -sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F -ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY -KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG -+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG -HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P -IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M -733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk -Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW -AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I -aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5 -mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa -XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ -qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9 +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx @@ -3611,42 +2999,90 @@ HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx -FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD -VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy -dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t -MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB -MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG -A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp -b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl -cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv -bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE -VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ -ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR -uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG -9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI -hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM -pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx -FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD -VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm -MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx -MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT -DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 -dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl -cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 -DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD -gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 -yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX -L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj -EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG -7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e -QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ -qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y +IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig +RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb +3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA +BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 +3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou +owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ +wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF +ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf +BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv +civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 +AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 +soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI +WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi +tJ/X5g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y +IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB +pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h +IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG +A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU +cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid +RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V +seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme +9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV +EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW +hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ +DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I +/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ +yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts +L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN +zl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig +Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk +MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg +Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD +VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy +dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ +QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq +1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp +2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK +DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape +az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF +3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 +oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM +g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 +mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd +BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U +nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX +dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ +MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL +/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX +CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa +ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW +2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 +N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 +Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB +As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp +5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu +1uwJ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF @@ -3670,149 +3106,79 @@ jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN ZetX2fNXlrtIzYE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS -MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp -bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw -VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy -YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy -dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2 -ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe -Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx -GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls -aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU -QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh -xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0 -aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr -IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h -gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK -O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO -fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw -lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL -hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID -AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP -NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t -wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM -7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh -gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n -oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs -yZyQ2uypQjyttgI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG -EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD -VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu -dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 -E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ -D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK -4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq -lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW -bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB -o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT -MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js -LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr -BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB -AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft -Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj -j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH -KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv -2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 -mfnGV/TJVTl4uix5yaaIK/QI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB -rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt -Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa -Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV -BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l -dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE -AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B -YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 -hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l -L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm -SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM -1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws -6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw -Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 -aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH -AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u -7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 -xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ -rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim -eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk -USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB -lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt -SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG -A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe -MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v -d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh -cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn -0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ -M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a -MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd -oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI -DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy -oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 -dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy -bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF -BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli -CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE -CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t -3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS -KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy -NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y -LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ -TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y -TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 -LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW -I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw -nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 -IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz -BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y -aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG -9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy -NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y -azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw -Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl -cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY -dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 -WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS -v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v -UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu -IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC -W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE +BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn +aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg +QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg +SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0 +MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD +VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8 +dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom +/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR +Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3 +4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z +5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0 +hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID +AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX +SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l +VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf +peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF +Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW ++qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL @@ -3892,139 +3258,6 @@ lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 7M2CYfE45k+XmCpajQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0f -zGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHi -TkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0G -CSqGSIb3DQEBBQUAA4GBAFgVKTk8d6PaXCUDfGD67gmZPCcQcMgMCeazh88K4hiW -NWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n0a3hUKw8fGJLj7qE1xIV -Gx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZRjXZ+Hxb ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh -c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy -MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp -emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X -DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw -FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg -UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo -YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 -MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK -VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm -Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID -AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J -h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul -uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68 -DzFc6PLZ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 -nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO -8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV -ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb -PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 -6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr -n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a -qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 -wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 -ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs -pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 -E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns -YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH -MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y -aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe -Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX -MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj -IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx -KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s -eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM -HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw -DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC -AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji -nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX -rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn -jBJ7xUS0rg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy -aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s -IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp -Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV -BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp -Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g -Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt -IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU -J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO -JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY -wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o -koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN -qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E -Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe -xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u -7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU -sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI -sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP -cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz -cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 -MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV -BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN -ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE -BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is -I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G -CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i -2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ -2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh -c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy -MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp -emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X -DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw -FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg -UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo -YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 -MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB -AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 -pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 -13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID -AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk -U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i -F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY -oJ2daZH9 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu @@ -4049,30 +3282,6 @@ F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 -GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ -+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd -U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm -NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY -ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ -ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 -CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq -g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm -fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c -2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ -bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv @@ -4095,34 +3304,6 @@ LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd 398znM/jra6O1I7mT1GvFpLgXPYHDw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx -IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs -cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v -dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0 -MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl -bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD -DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r -WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU -Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs -HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj -z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf -SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl -AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG -KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P -AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j -BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC -VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX -ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB -ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd -/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB -A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn -k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9 -iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv -2G0xffX8oRAHh84vWdw+WNs= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY @@ -4265,4 +3446,5 @@ t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu MdRAGmI0Nj81Aa6sY6A= ------END CERTIFICATE-----`) +-----END CERTIFICATE----- +`) From 6a26430d7e6516cd5e3c80bfa89f8738a8211dd8 Mon Sep 17 00:00:00 2001 From: DmitryStashkevich <34479135+DmitryStashkevich@users.noreply.github.com> Date: Thu, 27 Feb 2020 20:55:57 +0200 Subject: [PATCH 019/318] add admixer adapter (#1195) --- adapters/admixer/admixer.go | 184 ++++++++++++++++++ adapters/admixer/admixer_test.go | 10 + .../exemplary/optional-params.json | 104 ++++++++++ .../exemplary/simple-app-audio.json | 89 +++++++++ .../exemplary/simple-app-banner.json | 101 ++++++++++ .../exemplary/simple-app-native.json | 90 +++++++++ .../exemplary/simple-app-video.json | 111 +++++++++++ .../exemplary/simple-site-audio.json | 89 +++++++++ .../exemplary/simple-site-banner.json | 101 ++++++++++ .../exemplary/simple-site-native.json | 90 +++++++++ .../exemplary/simple-site-video.json | 111 +++++++++++ .../admixertest/params/race/audio.json | 5 + .../admixertest/params/race/banner.json | 5 + .../admixertest/params/race/native.json | 5 + .../admixertest/params/race/video.json | 5 + .../supplemental/bad-dsp-request-example.json | 70 +++++++ .../dsp-server-internal-error-example.json | 70 +++++++ .../supplemental/ext-unmarshall-error.json | 34 ++++ .../unknown-status-code-example.json | 70 +++++++ .../supplemental/wrong-zone-id-error.json | 30 +++ .../supplemental/zero-bid-request-error.json | 19 ++ adapters/admixer/params_test.go | 57 ++++++ adapters/admixer/usersync.go | 11 ++ adapters/admixer/usersync_test.go | 34 ++++ config/config.go | 2 + exchange/adapter_map.go | 2 + go.mod | 15 +- go.sum | 60 +++++- openrtb_ext/bidders.go | 2 + openrtb_ext/imp_admixer.go | 7 + static/bidder-info/admixer.yaml | 15 ++ static/bidder-params/admixer.json | 25 +++ usersync/usersyncers/syncer.go | 5 +- usersync/usersyncers/syncer_test.go | 1 + 34 files changed, 1623 insertions(+), 6 deletions(-) create mode 100644 adapters/admixer/admixer.go create mode 100644 adapters/admixer/admixer_test.go create mode 100644 adapters/admixer/admixertest/exemplary/optional-params.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-app-audio.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-app-banner.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-app-native.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-app-video.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-site-audio.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-site-banner.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-site-native.json create mode 100644 adapters/admixer/admixertest/exemplary/simple-site-video.json create mode 100644 adapters/admixer/admixertest/params/race/audio.json create mode 100644 adapters/admixer/admixertest/params/race/banner.json create mode 100644 adapters/admixer/admixertest/params/race/native.json create mode 100644 adapters/admixer/admixertest/params/race/video.json create mode 100644 adapters/admixer/admixertest/supplemental/bad-dsp-request-example.json create mode 100644 adapters/admixer/admixertest/supplemental/dsp-server-internal-error-example.json create mode 100644 adapters/admixer/admixertest/supplemental/ext-unmarshall-error.json create mode 100644 adapters/admixer/admixertest/supplemental/unknown-status-code-example.json create mode 100644 adapters/admixer/admixertest/supplemental/wrong-zone-id-error.json create mode 100644 adapters/admixer/admixertest/supplemental/zero-bid-request-error.json create mode 100644 adapters/admixer/params_test.go create mode 100644 adapters/admixer/usersync.go create mode 100644 adapters/admixer/usersync_test.go create mode 100644 openrtb_ext/imp_admixer.go create mode 100644 static/bidder-info/admixer.yaml create mode 100644 static/bidder-params/admixer.json diff --git a/adapters/admixer/admixer.go b/adapters/admixer/admixer.go new file mode 100644 index 00000000000..65a94f352ed --- /dev/null +++ b/adapters/admixer/admixer.go @@ -0,0 +1,184 @@ +package admixer + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" +) + +type AdmixerAdapter struct { + endpoint string +} + +func NewAdmixerBidder(endpoint string) *AdmixerAdapter { + return &AdmixerAdapter{endpoint: endpoint} +} + +type admixerImpExt struct { + CustomParams map[string]interface{} `json:"customParams"` +} + +func (a *AdmixerAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) { + rq, errs := a.makeRequest(request) + + if len(errs) > 0 { + errors = append(errors, errs...) + return + } + + if rq != nil { + requests = append(requests, rq) + } + + return +} + +func (a *AdmixerAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) { + var errs []error + var validImps []openrtb.Imp + + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impressions in request", + }} + } + + for _, imp := range request.Imp { + if err := preprocess(&imp); err != nil { + errs = append(errs, err) + continue + } + validImps = append(validImps, imp) + } + + if len(validImps) == 0 { + return nil, errs + } + + request.Imp = validImps + + reqJSON, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + return &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: reqJSON, + Headers: headers, + }, errs +} + +func preprocess(imp *openrtb.Imp) error { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + var admixerExt openrtb_ext.ExtImpAdmixer + if err := json.Unmarshal(bidderExt.Bidder, &admixerExt); err != nil { + return &errortypes.BadInput{ + Message: "Wrong Admixer bidder ext", + } + } + + //don't use regexp due to possible performance reduce + if len(admixerExt.ZoneId) != 36 { + return &errortypes.BadInput{ + Message: "ZoneId must be UUID/GUID", + } + } + + imp.TagID = admixerExt.ZoneId + imp.BidFloor = admixerExt.CustomBidFloor + + imp.Ext = nil + + if admixerExt.CustomParams != nil { + impExt := admixerImpExt{ + CustomParams: admixerExt.CustomParams, + } + var err error + if imp.Ext, err = json.Marshal(impExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + } + + return nil +} + +func (a *AdmixerAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode >= http.StatusInternalServerError { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode), + }} + } + + if response.StatusCode >= http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + //additional no content check + if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 { + return nil, nil + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), + }) + } + } + return bidResponse, nil +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + return openrtb_ext.BidTypeVideo + } else if imp.Native != nil { + return openrtb_ext.BidTypeNative + } else if imp.Audio != nil { + return openrtb_ext.BidTypeAudio + } + } + } + return openrtb_ext.BidTypeBanner +} diff --git a/adapters/admixer/admixer_test.go b/adapters/admixer/admixer_test.go new file mode 100644 index 00000000000..b379e8b2910 --- /dev/null +++ b/adapters/admixer/admixer_test.go @@ -0,0 +1,10 @@ +package admixer + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "admixertest", NewAdmixerBidder("http://inv-nets.admixer.net/pbs.aspx")) +} diff --git a/adapters/admixer/admixertest/exemplary/optional-params.json b/adapters/admixer/admixertest/exemplary/optional-params.json new file mode 100644 index 00000000000..42a55ec95e8 --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/optional-params.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2", + "customFloor": 0.1, + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2", + "customFloor": 0.1, + "customParams": { + "foo": [ + "bar", + "baz" + ] + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2", + "bidfloor": 0.1, + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2", + "bidfloor": 0.1, + "ext": { + "customParams": { + "foo": [ + "bar", + "baz" + ] + } + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-app-audio.json b/adapters/admixer/admixertest/exemplary/simple-app-audio.json new file mode 100644 index 00000000000..b8c39ead95e --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-app-audio.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "ext": { + "bidder": { + "zone": "473e443c-43d0-423d-a8d7-a302637a01d8" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "tagid": "473e443c-43d0-423d-a8d7-a302637a01d8" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid" + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-app-banner.json b/adapters/admixer/admixertest/exemplary/simple-app-banner.json new file mode 100644 index 00000000000..aff4ccddd64 --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-app-banner.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 90, + "w": 728 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-app-native.json b/adapters/admixer/admixertest/exemplary/simple-app-native.json new file mode 100644 index 00000000000..38c005c651c --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-app-native.json @@ -0,0 +1,90 @@ + +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}" + }, + "ext": { + "bidder": { + "zone": "b1fbebfc-7155-4922-bb86-615e7f3d6eef" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}" + }, + "tagid": "b1fbebfc-7155-4922-bb86-615e7f3d6eef" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid" + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-app-video.json b/adapters/admixer/admixertest/exemplary/simple-app-video.json new file mode 100644 index 00000000000..627023fa1e6 --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-app-video.json @@ -0,0 +1,111 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "zone": "ac7fa772-d7be-48cc-820b-e21728e434fe" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 1024, + "h": 576 + }, + "tagid": "ac7fa772-d7be-48cc-820b-e21728e434fe" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "admixer", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 1024, + "h": 576 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-site-audio.json b/adapters/admixer/admixertest/exemplary/simple-site-audio.json new file mode 100644 index 00000000000..5a1d6531a85 --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-site-audio.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "ext": { + "bidder": { + "zone": "473e443c-43d0-423d-a8d7-a302637a01d8" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "tagid": "473e443c-43d0-423d-a8d7-a302637a01d8" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid" + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-site-banner.json b/adapters/admixer/admixertest/exemplary/simple-site-banner.json new file mode 100644 index 00000000000..bd50aba8d1a --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-site-banner.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 90, + "w": 728 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-site-native.json b/adapters/admixer/admixertest/exemplary/simple-site-native.json new file mode 100644 index 00000000000..246d02025b1 --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-site-native.json @@ -0,0 +1,90 @@ + +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}" + }, + "ext": { + "bidder": { + "zone": "b1fbebfc-7155-4922-bb86-615e7f3d6eef" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":60,\"hmin\":60,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":75}},{\"id\":4,\"required\":0,\"data\":{\"type\":6,\"len\":1000}},{\"id\":5,\"required\":0,\"data\":{\"type\":7,\"len\":1000}},{\"id\":6,\"required\":0,\"data\":{\"type\":11,\"len\":1000}}]}" + }, + "tagid": "b1fbebfc-7155-4922-bb86-615e7f3d6eef" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "admixer", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid" + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/exemplary/simple-site-video.json b/adapters/admixer/admixertest/exemplary/simple-site-video.json new file mode 100644 index 00000000000..42d771ce86b --- /dev/null +++ b/adapters/admixer/admixertest/exemplary/simple-site-video.json @@ -0,0 +1,111 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "zone": "ac7fa772-d7be-48cc-820b-e21728e434fe" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 1024, + "h": 576 + }, + "tagid": "ac7fa772-d7be-48cc-820b-e21728e434fe" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "admixer", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 1024, + "h": 576 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/admixer/admixertest/params/race/audio.json b/adapters/admixer/admixertest/params/race/audio.json new file mode 100644 index 00000000000..f9aa771e4b1 --- /dev/null +++ b/adapters/admixer/admixertest/params/race/audio.json @@ -0,0 +1,5 @@ +{ + "zone": "473e443c-43d0-423d-a8d7-a302637a01d8", + "customFloor": 0.1, + "customParams": {"foo": "bar"} +} \ No newline at end of file diff --git a/adapters/admixer/admixertest/params/race/banner.json b/adapters/admixer/admixertest/params/race/banner.json new file mode 100644 index 00000000000..03510f7e1ca --- /dev/null +++ b/adapters/admixer/admixertest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2", + "customFloor": 0.1, + "customParams": {"foo": "bar"} +} diff --git a/adapters/admixer/admixertest/params/race/native.json b/adapters/admixer/admixertest/params/race/native.json new file mode 100644 index 00000000000..65712a30228 --- /dev/null +++ b/adapters/admixer/admixertest/params/race/native.json @@ -0,0 +1,5 @@ +{ + "zone": "b1fbebfc-7155-4922-bb86-615e7f3d6eef", + "customFloor": 0.1, + "customParams": {"foo": "bar"} +} \ No newline at end of file diff --git a/adapters/admixer/admixertest/params/race/video.json b/adapters/admixer/admixertest/params/race/video.json new file mode 100644 index 00000000000..5e9bc6e59fd --- /dev/null +++ b/adapters/admixer/admixertest/params/race/video.json @@ -0,0 +1,5 @@ +{ + "zone": "ac7fa772-d7be-48cc-820b-e21728e434fe", + "customFloor": 0.1, + "customParams": {"foo": "bar"} +} diff --git a/adapters/admixer/admixertest/supplemental/bad-dsp-request-example.json b/adapters/admixer/admixertest/supplemental/bad-dsp-request-example.json new file mode 100644 index 00000000000..5256c14050b --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/bad-dsp-request-example.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Bad request to dsp", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/admixertest/supplemental/dsp-server-internal-error-example.json b/adapters/admixer/admixertest/supplemental/dsp-server-internal-error-example.json new file mode 100644 index 00000000000..1c06eadce44 --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/dsp-server-internal-error-example.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Dsp server internal error", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/admixertest/supplemental/ext-unmarshall-error.json b/adapters/admixer/admixertest/supplemental/ext-unmarshall-error.json new file mode 100644 index 00000000000..e14a26356f8 --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/ext-unmarshall-error.json @@ -0,0 +1,34 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "format": [ + { + "w": 728, + "h": 90 + } + ], + "ext": { + "bidder": { + "zone": [ + "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + ] + } + } + } + ], + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Wrong Admixer bidder ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/admixertest/supplemental/unknown-status-code-example.json b/adapters/admixer/admixertest/supplemental/unknown-status-code-example.json new file mode 100644 index 00000000000..972f2f5dd01 --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/unknown-status-code-example.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "zone": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://inv-nets.admixer.net/pbs.aspx", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "3e56bd58-865c-47ce-af7f-a918108c3fd2" + } + ] + } + }, + "mockResponse": { + "status": 301, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 301", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/admixertest/supplemental/wrong-zone-id-error.json b/adapters/admixer/admixertest/supplemental/wrong-zone-id-error.json new file mode 100644 index 00000000000..2f2dcec1a50 --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/wrong-zone-id-error.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "format": [{ + "w": 728, + "h": 90 + }], + "ext": { + "bidder": { + "zone": "12345" + } + } + } + ], + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "ZoneId must be UUID/GUID", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/admixertest/supplemental/zero-bid-request-error.json b/adapters/admixer/admixertest/supplemental/zero-bid-request-error.json new file mode 100644 index 00000000000..a43d9b5fa65 --- /dev/null +++ b/adapters/admixer/admixertest/supplemental/zero-bid-request-error.json @@ -0,0 +1,19 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + ], + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impressions in request", + "comparison": "literal" + } + ] +} diff --git a/adapters/admixer/params_test.go b/adapters/admixer/params_test.go new file mode 100644 index 00000000000..71cccb6a3da --- /dev/null +++ b/adapters/admixer/params_test.go @@ -0,0 +1,57 @@ +package admixer + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +// This file actually intends to test static/bidder-params/admixer.json +// +// These also validate the format of the external API: request.imp[i].ext.admixer + +// TestValidParams makes sure that the admixer schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdmixer, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected admixer params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the admixer schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdmixer, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA21"}`, + `{"zone": "9ff668a2-4122-462e-aaf8-36ea3a54ba21"}`, + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA21", "customFloor": 0.1}`, + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA21", "customParams": {"foo": "bar"}}`, + `{"zone": "9ff668a2-4122-462e-aaf8-36ea3a54ba21", "customFloor": 0.1, "customParams": {"foo": ["bar", "baz"]}}`, +} + +var invalidParams = []string{ + `{"zone": "123"}`, + `{"zone": ""}`, + `{"zone": "ZFF668A2-4122-462E-AAF8-36EA3A54BA21"}`, + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA211"}`, + `{"zone": "123", "customFloor": "0.1"}`, + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA21", "customFloor": -0.1}`, + `{"zone": "9FF668A2-4122-462E-AAF8-36EA3A54BA21", "customParams": "foo: bar"}`, +} diff --git a/adapters/admixer/usersync.go b/adapters/admixer/usersync.go new file mode 100644 index 00000000000..0a7f50ab79a --- /dev/null +++ b/adapters/admixer/usersync.go @@ -0,0 +1,11 @@ +package admixer + +import ( + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" + "text/template" +) + +func NewAdmixerSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("admixer", 511, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go new file mode 100644 index 00000000000..a5715c64a46 --- /dev/null +++ b/adapters/admixer/usersync_test.go @@ -0,0 +1,34 @@ +package admixer + +import ( + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + "testing" + "text/template" +) + +func TestAdmixerSyncer(t *testing.T) { + syncURL := "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl=localhost%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAdmixerSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "A", + Consent: "B", + }, + CCPA: ccpa.Policy{ + Value: "C", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://inv-nets.admixer.net/adxcm.aspx?gdpr=A&gdpr_consent=B&us_privacy=C&redir=1&rurl=localhost%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24%24visitor_cookie%24%24", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 511, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 52686422039..f582201d517 100644 --- a/config/config.go +++ b/config/config.go @@ -491,6 +491,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernelAdn, "https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") @@ -673,6 +674,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx") v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}") + v.SetDefault("adapters.admixer.endpoint", "http://inv-nets.admixer.net/pbs.aspx") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index d169c1204bf..4c6da00f337 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -12,6 +12,7 @@ import ( "github.com/prebid/prebid-server/adapters/adform" "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" + "github.com/prebid/prebid-server/adapters/admixer" "github.com/prebid/prebid-server/adapters/adoppler" "github.com/prebid/prebid-server/adapters/adpone" "github.com/prebid/prebid-server/adapters/adtelligent" @@ -72,6 +73,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdform: adform.NewAdformBidder(client, cfg.Adapters[string(openrtb_ext.BidderAdform)].Endpoint), openrtb_ext.BidderAdkernel: adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint), openrtb_ext.BidderAdkernelAdn: adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint), + openrtb_ext.BidderAdmixer: admixer.NewAdmixerBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdmixer))].Endpoint), openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), openrtb_ext.BidderAdpone: adpone.NewAdponeBidder(cfg.Adapters[string(openrtb_ext.BidderAdpone)].Endpoint), openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), diff --git a/go.mod b/go.mod index 5c837c2ee7b..af4bf5570a5 100644 --- a/go.mod +++ b/go.mod @@ -7,17 +7,23 @@ require ( github.com/DATA-DOG/go-sqlmock v1.3.0 github.com/NYTimes/gziphandler v1.1.1 github.com/OneOfOne/xxhash v1.2.5 // indirect + github.com/aerospike/aerospike-client-go v2.7.2+incompatible github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/blang/semver v3.5.1+incompatible + github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 github.com/cespare/xxhash v1.0.0 // indirect github.com/chasex/glog v0.0.0-20160217080310-c62392af379c github.com/coocood/freecache v1.0.1 + github.com/didip/tollbooth v4.0.2+incompatible github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd + github.com/go-redis/redis v6.15.7+incompatible + github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/snappy v0.0.1 github.com/hashicorp/hcl v1.0.0 // indirect github.com/influxdata/influxdb v1.6.1 // indirect github.com/julienschmidt/httprouter v1.1.0 @@ -31,8 +37,10 @@ require ( github.com/mxmCherry/openrtb v11.0.0+incompatible github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.2.0 // indirect github.com/prebid/go-gdpr v0.6.0 + github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect @@ -40,14 +48,15 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 github.com/rs/cors v1.5.0 github.com/sergi/go-diff v1.0.0 // indirect + github.com/sirupsen/logrus v1.4.2 github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.1.1 // indirect github.com/spf13/cast v1.2.0 // indirect github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect github.com/spf13/pflag v1.0.2 // indirect github.com/spf13/viper v1.1.0 - github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.3.0 + github.com/valyala/fasthttp v1.9.0 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -55,8 +64,10 @@ require ( github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect - golang.org/x/net v0.0.0-20180906233101-161cd47e91fd + github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect + golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/text v0.3.0 + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/yaml.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index 57fcbb76428..06f07b1ece0 100644 --- a/go.sum +++ b/go.sum @@ -6,35 +6,57 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/aerospike/aerospike-client-go v2.7.2+incompatible h1:bWbRf8trg1FhKF7u43KLGNfOH60RlvIgQjpaS107DZ8= +github.com/aerospike/aerospike-client-go v2.7.2+incompatible/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.0.0 h1:naDmySfoNg0nKS62/ujM6e71ZgM2AoVdaqGwMG0w18A= github.com/cespare/xxhash v1.0.0/go.mod h1:fX/lfQBkSCDXZSUgv6jVIu/EVA3/JNseAX5asI4c4T4= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c h1:eXqCBUHfmjbeDqcuvzjsd+bM6A+bnwo5N9FVbV6m5/s= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c/go.mod h1:omJZNg0Qu76bxJd+ExohVo8uXzNcGOk2bv7vel460xk= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/coocood/freecache v1.0.1 h1:oFyo4msX2c0QIKU+kuMJUwsKamJ+AKc2JJrKcMszJ5M= github.com/coocood/freecache v1.0.1/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M= +github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd h1:biTJQdqouE5by89AAffXG8++TY+9Fsdrg5rinbt3tHk= github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U= +github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 h1:ZZVxQRCm4ewuoqqLBJ7LHpsk4OGx2PkyCsRKLq4oHgE= +github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -45,6 +67,17 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -66,18 +99,20 @@ github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prebid/go-gdpr v0.6.0 h1:/GKrygGkUbsgd96HIkjAu7/6CHtRedvcojRtfAd4Igc= github.com/prebid/go-gdpr v0.6.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= +github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= +github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0= @@ -88,6 +123,8 @@ github.com/rs/cors v1.5.0 h1:dgSHE6+ia18arGOTIYQKKGWLvEbGvmbNE6NfxhoNHUY= github.com/rs/cors v1.5.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= @@ -103,8 +140,14 @@ github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7Sr github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 h1:Va10CytCCYRm4xBTses5ZDeDjeIQjhaiC9nRCe/yflI= github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303/go.mod h1:Xdcad1nGVhQfhoV0go+/4WaI/RZkWlvfjkVCdpMTxPY= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= @@ -119,21 +162,34 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= +github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 6e70ef4b6fa..3ae443410b9 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -25,6 +25,7 @@ const ( BidderAdkernel BidderName = "adkernel" BidderAdkernelAdn BidderName = "adkernelAdn" BidderAdpone BidderName = "adpone" + BidderAdmixer BidderName = "admixer" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" BidderApplogy BidderName = "applogy" @@ -80,6 +81,7 @@ var BidderMap = map[string]BidderName{ "adform": BidderAdform, "adkernel": BidderAdkernel, "adkernelAdn": BidderAdkernelAdn, + "admixer": BidderAdmixer, "adpone": BidderAdpone, "adtelligent": BidderAdtelligent, "advangelists": BidderAdvangelists, diff --git a/openrtb_ext/imp_admixer.go b/openrtb_ext/imp_admixer.go new file mode 100644 index 00000000000..ce122ae0029 --- /dev/null +++ b/openrtb_ext/imp_admixer.go @@ -0,0 +1,7 @@ +package openrtb_ext + +type ExtImpAdmixer struct { + ZoneId string `json:"zone"` + CustomBidFloor float64 `json:"customFloor"` + CustomParams map[string]interface{} `json:"customParams"` +} diff --git a/static/bidder-info/admixer.yaml b/static/bidder-info/admixer.yaml new file mode 100644 index 00000000000..64ad2024058 --- /dev/null +++ b/static/bidder-info/admixer.yaml @@ -0,0 +1,15 @@ +maintainer: + email: "prebid@admixer.net" +capabilities: + app: + mediaTypes: + - banner + - video + - native + - audio + site: + mediaTypes: + - banner + - video + - native + - audio \ No newline at end of file diff --git a/static/bidder-params/admixer.json b/static/bidder-params/admixer.json new file mode 100644 index 00000000000..886e33ff2bb --- /dev/null +++ b/static/bidder-params/admixer.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Admixer Adapter Params", + "description": "A schema which validates params accepted by the Admixer adapter", + + "type": "object", + "properties": { + "zone": { + "type": "string", + "description": "Zone ID.", + "pattern": "^([a-fA-F\\d\\-]{36})$" + }, + "customFloor": { + "type": "number", + "description": "The minimum CPM price in USD.", + "minimum": 0 + }, + "customParams": { + "type": "object", + "description": "User-defined targeting key-value pairs." + } + }, + + "required": ["zone"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 5447cd28800..7f65c7f476f 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -4,13 +4,13 @@ import ( "strings" "text/template" - "github.com/prebid/prebid-server/adapters/adpone" - "github.com/golang/glog" ttx "github.com/prebid/prebid-server/adapters/33across" "github.com/prebid/prebid-server/adapters/adform" "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" + "github.com/prebid/prebid-server/adapters/admixer" + "github.com/prebid/prebid-server/adapters/adpone" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" "github.com/prebid/prebid-server/adapters/appnexus" @@ -68,6 +68,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdform, adform.NewAdformSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernel, adkernel.NewAdkernelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernelAdn, adkernelAdn.NewAdkernelAdnSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAdmixer, admixer.NewAdmixerSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdpone, adpone.NewadponeSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdtelligent, adtelligent.NewAdtelligentSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 87a9caebf96..2a8d1fd1b0b 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -18,6 +18,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdform): syncConfig, string(openrtb_ext.BidderAdkernel): syncConfig, string(openrtb_ext.BidderAdkernelAdn): syncConfig, + string(openrtb_ext.BidderAdmixer): syncConfig, string(openrtb_ext.BidderAdpone): syncConfig, string(openrtb_ext.BidderAdtelligent): syncConfig, string(openrtb_ext.BidderAdvangelists): syncConfig, From 2e806517d34fa7bb9a8dfcd819915cf55e96b8a6 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Tue, 3 Mar 2020 06:59:15 -0800 Subject: [PATCH 020/318] Adding copying of gdpr consent string to openrtb bid request (#1189) * Adding copying of gdpr consent string to openrtb bid request * Updated video request to use OpenRTB Video and User objects * Fixing unit test failure message * Updates from code review comments * Updating unit test initialization * Updated mimes array construction --- endpoints/openrtb2/video_auction.go | 50 +++++++------- endpoints/openrtb2/video_auction_test.go | 66 +++++++++++++++--- openrtb_ext/bid_request_video.go | 87 +----------------------- 3 files changed, 81 insertions(+), 122 deletions(-) diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 7c9651af747..feb8de193e7 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -328,12 +328,12 @@ func max(a, b int) int { return b } -func createImpressionTemplate(imp openrtb.Imp, video openrtb_ext.SimplifiedVideo) openrtb.Imp { +func createImpressionTemplate(imp openrtb.Imp, video *openrtb.Video) openrtb.Imp { imp.Video = &openrtb.Video{} imp.Video.W = video.W imp.Video.H = video.H imp.Video.Protocols = video.Protocols - imp.Video.MIMEs = video.Mimes + imp.Video.MIMEs = video.MIMEs return imp } @@ -471,14 +471,7 @@ func mergeData(videoRequest *openrtb_ext.BidRequestVideo, bidRequest *openrtb.Bi bidRequest.Device = &videoRequest.Device } - if &videoRequest.User != nil { - bidRequest.User = &openrtb.User{ - BuyerUID: videoRequest.User.Buyeruids["appnexus"], //TODO: map to string merging - Yob: videoRequest.User.Yob, - Gender: videoRequest.User.Gender, - Keywords: videoRequest.User.Keywords, - } - } + bidRequest.User = videoRequest.User if len(videoRequest.BCat) != 0 { bidRequest.BCat = videoRequest.BCat @@ -660,27 +653,30 @@ func (deps *endpointDeps) validateVideoRequest(req *openrtb_ext.BidRequestVideo) } } - if len(req.Video.Mimes) == 0 { - err := errors.New("request missing required field: Video.Mimes") - errL = append(errL, err) - } else { - mimes := make([]string, 0, 0) - for _, mime := range req.Video.Mimes { - if mime != "" { - mimes = append(mimes, mime) + if req.Video != nil { + if len(req.Video.MIMEs) == 0 { + err := errors.New("request missing required field: Video.Mimes") + errL = append(errL, err) + } else { + mimes := make([]string, 0, len(req.Video.MIMEs)) + for _, mime := range req.Video.MIMEs { + if mime != "" { + mimes = append(mimes, mime) + } } + if len(mimes) == 0 { + err := errors.New("request missing required field: Video.Mimes, mime types contains empty strings only") + errL = append(errL, err) + } + req.Video.MIMEs = mimes } - if len(mimes) == 0 { - err := errors.New("request missing required field: Video.Mimes, mime types contains empty strings only") + + if len(req.Video.Protocols) == 0 { + err := errors.New("request missing required field: Video.Protocols") errL = append(errL, err) } - if len(mimes) > 0 { - req.Video.Mimes = mimes - } - } - - if len(req.Video.Protocols) == 0 { - err := errors.New("request missing required field: Video.Protocols") + } else { + err := errors.New("request missing required field: Video") errL = append(errL, err) } diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index cd87041055a..dfe2a6a50b8 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -233,8 +233,8 @@ func TestVideoEndpointValidationsPositive(t *testing.T) { IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ PrimaryAdserver: 1, }, - Video: openrtb_ext.SimplifiedVideo{ - Mimes: mimes, + Video: &openrtb.Video{ + MIMEs: mimes, Protocols: videoProtocols, }, } @@ -271,8 +271,8 @@ func TestVideoEndpointValidationsCritical(t *testing.T) { IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ PrimaryAdserver: 0, }, - Video: openrtb_ext.SimplifiedVideo{ - Mimes: mimes, + Video: &openrtb.Video{ + MIMEs: mimes, Protocols: videoProtocols, }, } @@ -345,8 +345,8 @@ func TestVideoEndpointValidationsPodErrors(t *testing.T) { IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ PrimaryAdserver: 1, }, - Video: openrtb_ext.SimplifiedVideo{ - Mimes: mimes, + Video: &openrtb.Video{ + MIMEs: mimes, Protocols: videoProtocols, }, } @@ -418,8 +418,8 @@ func TestVideoEndpointValidationsSiteAndApp(t *testing.T) { IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ PrimaryAdserver: 1, }, - Video: openrtb_ext.SimplifiedVideo{ - Mimes: mimes, + Video: &openrtb.Video{ + MIMEs: mimes, Protocols: videoProtocols, }, } @@ -473,8 +473,8 @@ func TestVideoEndpointValidationsSiteMissingRequiredField(t *testing.T) { IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ PrimaryAdserver: 1, }, - Video: openrtb_ext.SimplifiedVideo{ - Mimes: mimes, + Video: &openrtb.Video{ + MIMEs: mimes, Protocols: videoProtocols, }, } @@ -484,6 +484,43 @@ func TestVideoEndpointValidationsSiteMissingRequiredField(t *testing.T) { assert.Len(t, podErrors, 0, "Pod errors should be empty") } +func TestVideoEndpointValidationsMissingVideo(t *testing.T) { + ex := &mockExchangeVideo{} + deps := mockDeps(t, ex) + deps.cfg.VideoStoredRequestRequired = true + + req := openrtb_ext.BidRequestVideo{ + StoredRequestId: "123", + PodConfig: openrtb_ext.PodConfig{ + DurationRangeSec: []int{15, 30}, + RequireExactDuration: true, + Pods: []openrtb_ext.Pod{ + { + PodId: 1, + AdPodDurationSec: 30, + ConfigId: "qwerty", + }, + { + PodId: 2, + AdPodDurationSec: 30, + ConfigId: "qwerty", + }, + }, + }, + App: &openrtb.App{ + Bundle: "pbs.com", + }, + IncludeBrandCategory: &openrtb_ext.IncludeBrandCategory{ + PrimaryAdserver: 1, + }, + } + + errors, podErrors := deps.validateVideoRequest(&req) + assert.Len(t, podErrors, 0, "Pod errors should be empty") + assert.Len(t, errors, 1, "Errors array should contain 1 error message") + assert.Equal(t, "request missing required field: Video", errors[0].Error(), "Errors array should contain message regarding missing Video field") +} + func TestVideoBuildVideoResponseMissedCacheForOneBid(t *testing.T) { openRtbBidResp := openrtb.BidResponse{} podErrors := make([]PodError, 0) @@ -633,6 +670,13 @@ func TestMergeOpenRTBToVideoRequest(t *testing.T) { Ext: json.RawMessage(`{"gdpr":1,"us_privacy":"1NYY","existing":"any","consent":"anyConsent"}`), } + videoReq.User = &openrtb.User{ + BuyerUID: "test UID", + Yob: 1980, + Keywords: "test keywords", + Ext: json.RawMessage(`{"consent":"test string"}`), + } + mergeData(videoReq, bidReq) assert.Equal(t, videoReq.BCat, bidReq.BCat, "BCat is incorrect") @@ -647,6 +691,8 @@ func TestMergeOpenRTBToVideoRequest(t *testing.T) { assert.Equal(t, videoReq.Site.Page, bidReq.Site.Page, "Device.Site.Page is incorrect") assert.Equal(t, videoReq.Regs, bidReq.Regs, "Regs is incorrect") + + assert.Equal(t, videoReq.User, bidReq.User, "User is incorrect") } func TestHandleError(t *testing.T) { diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go index f7ddf203294..18865108433 100644 --- a/openrtb_ext/bid_request_video.go +++ b/openrtb_ext/bid_request_video.go @@ -43,7 +43,7 @@ type BidRequestVideo struct { // object; optional // Description: // Container object for the user of of the actual device - User SimplifiedUser `json:"user,omitempty"` + User *openrtb.User `json:"user,omitempty"` // Attribute: // device @@ -67,7 +67,7 @@ type BidRequestVideo struct { // object; required // Description: // Player container object - Video SimplifiedVideo `json:"video,omitempty"` + Video *openrtb.Video `json:"video,omitempty"` // Attribute: // content @@ -225,86 +225,3 @@ type Cacheconfig struct { // Time to Live for a cache entry specified in seconds Ttl int `json:"ttl"` } - -type Gdpr struct { - // Attribute: - // consentrequired - // Type: - // boolean; optional - // Indicates whether GDPR is in effect - ConsentRequired bool `json:"consentrequired"` - - // Attribute: - // consentstring - // Type: - // string; optional - // Contains the data structure developed by the GDPR - ConsentString string `json:"consentstring"` -} - -type SimplifiedUser struct { - // Attribute: - // buyeruids - // Type: - // map; optional - // ID of the stored config that corresponds to a single pod request - Buyeruids map[string]string `json:"buyeruids"` - - // Attribute: - // gdpr - // Type: - // object; optional - // Container object for GDPR - Gdpr Gdpr `json:"gdpr"` - - // Attribute: - // yob - // Type: - // int; optional - // Year of birth as a 4-digit integer - Yob int64 `json:"yob"` - - // Attribute: - // gender - // Type: - // string; optional - // Gender, where “M” = male, “F” = female, “O” = known to be other - Gender string `json:"gender"` - - // Attribute: - // keywords - // Type: - // string; optional - // Comma separated list of keywords, interests, or intent. - Keywords string `json:"keywords"` -} - -type SimplifiedVideo struct { - // Attribute: - // w - // Type: - // uint64; optional - // Width of video - W uint64 `json:"w"` - - // Attribute: - // h - // Type: - // uint64; optional - // Height of video - H uint64 `json:"h"` - - // Attribute: - // mimes - // Type: - // array of strings; optional - // Video mime types - Mimes []string `json:"mimes"` - - // Attribute: - // protocols - // Type: - // array of objects; optional - // protocols - Protocols []openrtb.Protocol `json:"protocols"` -} From c6919ee17741b2ed5f3ffbb02d6d24ecefc629be Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Wed, 4 Mar 2020 07:28:54 -0800 Subject: [PATCH 021/318] fix conversant sync pixel (#1208) --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index f582201d517..7c9ffa71672 100644 --- a/config/config.go +++ b/config/config.go @@ -497,7 +497,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConsumable, "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/prebid/match/bounce/current?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26networkId%3D72582%26version%3D1%26uid%3D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26networkId%3D72582%26version%3D1%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") From f03dfa56d7689502e462ad306eb273075237fae7 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 4 Mar 2020 10:25:38 -0800 Subject: [PATCH 022/318] openx adapter: forward bid response currency in openx adapter if set (#1211) it was always set to the default USD before --- adapters/openx/openx.go | 5 +++++ adapters/openx/openx_test.go | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go index dd176813820..63e8e697869 100644 --- a/adapters/openx/openx.go +++ b/adapters/openx/openx.go @@ -169,6 +169,11 @@ func (a *OpenxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalReq bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + // overrride default currency + if bidResp.Cur != "" { + bidResponse.Currency = bidResp.Cur + } + for _, sb := range bidResp.SeatBid { for i := range sb.Bid { bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ diff --git a/adapters/openx/openx_test.go b/adapters/openx/openx_test.go index f7765d846ad..f79eb062531 100644 --- a/adapters/openx/openx_test.go +++ b/adapters/openx/openx_test.go @@ -1,11 +1,48 @@ package openx import ( + "encoding/json" "testing" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/stretchr/testify/assert" ) func TestJsonSamples(t *testing.T) { adapterstest.RunJSONBidderTest(t, "openxtest", NewOpenxBidder("http://rtb.openx.net/prebid")) } + +func TestResponseWithCurrencies(t *testing.T) { + assertCurrencyInBidResponse(t, "USD", nil) + + currency := "USD" + assertCurrencyInBidResponse(t, "USD", ¤cy) + + currency = "EUR" + assertCurrencyInBidResponse(t, "EUR", ¤cy) +} + +func assertCurrencyInBidResponse(t *testing.T, expectedCurrency string, currency *string) { + + bidder := NewOpenxBidder("http://rtb.openx.net/prebid") + prebidRequest := &openrtb.BidRequest{ + Imp: []openrtb.Imp{}, + } + mockedBidResponse := &openrtb.BidResponse{} + if currency != nil { + mockedBidResponse.Cur = *currency + } + body, _ := json.Marshal(mockedBidResponse) + responseData := &adapters.ResponseData{ + StatusCode: 200, + Body: body, + } + bidResponse, errs := bidder.MakeBids(prebidRequest, nil, responseData) + + if errs != nil { + t.Fatalf("Failed to make bids %v", errs) + } + assert.Equal(t, expectedCurrency, bidResponse.Currency) +} From 8686f03a46ad51ccc4a96f9756628abdc8e60176 Mon Sep 17 00:00:00 2001 From: guscarreon Date: Thu, 5 Mar 2020 10:41:38 -0500 Subject: [PATCH 023/318] add ucfunnel adapter (#1192) --- adapters/ucfunnel/params_test.go | 47 +++++ adapters/ucfunnel/ucfunnel.go | 150 ++++++++++++++++ adapters/ucfunnel/ucfunnel_test.go | 163 ++++++++++++++++++ .../ucfunneltest/exemplary/ucfunnel.json | 103 +++++++++++ .../ucfunneltest/params/race/banner.json | 5 + .../ucfunneltest/params/race/video.json | 5 + adapters/ucfunnel/usersync.go | 12 ++ adapters/ucfunnel/usersync_test.go | 30 ++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_ucfunnel.go | 7 + static/bidder-info/ucfunnel.yaml | 11 ++ static/bidder-params/ucfunnel.json | 17 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 16 files changed, 559 insertions(+) create mode 100644 adapters/ucfunnel/params_test.go create mode 100644 adapters/ucfunnel/ucfunnel.go create mode 100644 adapters/ucfunnel/ucfunnel_test.go create mode 100644 adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json create mode 100644 adapters/ucfunnel/ucfunneltest/params/race/banner.json create mode 100644 adapters/ucfunnel/ucfunneltest/params/race/video.json create mode 100644 adapters/ucfunnel/usersync.go create mode 100644 adapters/ucfunnel/usersync_test.go create mode 100644 openrtb_ext/imp_ucfunnel.go create mode 100644 static/bidder-info/ucfunnel.yaml create mode 100644 static/bidder-params/ucfunnel.json diff --git a/adapters/ucfunnel/params_test.go b/adapters/ucfunnel/params_test.go new file mode 100644 index 00000000000..4faec8739da --- /dev/null +++ b/adapters/ucfunnel/params_test.go @@ -0,0 +1,47 @@ +package ucfunnel + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +// This file actually intends to test static/bidder-params/ucfunnel.json +// +// These also validate the format of the external API: request.imp[i].ext.ucfunnel + +// TestValidParams makes sure that the ucfunnel schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderUcfunnel, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected ucfunnel params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the ucfunnel schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderUcfunnel, json.RawMessage(invalidParam)); err != nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"adunitid": "ad-83444226E44368D1E32E49EEBE6D29","partnerid": "par-2EDDB423AA24474188B843EE4842932"}`, +} + +var invalidParams = []string{ + `{"adunitid": "","partnerid": ""}`, +} diff --git a/adapters/ucfunnel/ucfunnel.go b/adapters/ucfunnel/ucfunnel.go new file mode 100644 index 00000000000..2c64e81205b --- /dev/null +++ b/adapters/ucfunnel/ucfunnel.go @@ -0,0 +1,150 @@ +package ucfunnel + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "net/url" +) + +type UcfunnelAdapter struct { + URI string +} + +func NewUcfunnelBidder(endpoint string) *UcfunnelAdapter { + return &UcfunnelAdapter{ + URI: endpoint} +} + +func (a *UcfunnelAdapter) Name() string { + return "ucfunnel" +} + +func (a *UcfunnelAdapter) SkipNoCookies() bool { + return false +} + +func (a *UcfunnelAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var errs []error + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + var bidReq openrtb.BidRequest + if err := json.Unmarshal(externalRequest.Body, &bidReq); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType := getBidType(bidReq, sb.Bid[i].ImpID) + if (bidType == openrtb_ext.BidTypeBanner) || (bidType == openrtb_ext.BidTypeVideo) { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +func (a *UcfunnelAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + errs := make([]error, 0, len(request.Imp)) + + // If all the requests were malformed, don't bother making a server call with no impressions. + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("No impression in the bid request\n"), + }} + } + + partnerId, partnerErr := getPartnerId(request) + if partnerErr != nil { + return nil, partnerErr + } + + reqJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json") + + uri := a.URI + "/" + url.PathEscape(partnerId) + "/request" + return []*adapters.RequestData{{ + Method: "POST", + Uri: uri, + Body: reqJSON, + Headers: headers, + }}, errs +} + +func getPartnerId(request *openrtb.BidRequest) (string, []error) { + var ext ExtBidderUcfunnel + var errs = []error{} + err := json.Unmarshal(request.Imp[0].Ext, &ext) + if err != nil { + errs = append(errs, err) + return "", errs + } + errs = checkBidderParameter(ext) + if errs != nil { + return "", errs + } + return ext.Bidder.PartnerId, nil +} + +func checkBidderParameter(ext ExtBidderUcfunnel) []error { + var errs = []error{} + if len(ext.Bidder.PartnerId) == 0 || len(ext.Bidder.AdUnitId) == 0 { + errs = append(errs, fmt.Errorf("No PartnerId or AdUnitId in the bid request\n")) + return errs + } + return nil +} + +func getBidType(bidReq openrtb.BidRequest, impid string) openrtb_ext.BidType { + for i := range bidReq.Imp { + if bidReq.Imp[i].ID == impid { + if bidReq.Imp[i].Banner != nil { + return openrtb_ext.BidTypeBanner + } else if bidReq.Imp[i].Video != nil { + return openrtb_ext.BidTypeVideo + } else if bidReq.Imp[i].Audio != nil { + return openrtb_ext.BidTypeAudio + } else if bidReq.Imp[i].Native != nil { + return openrtb_ext.BidTypeNative + } + } + } + return openrtb_ext.BidTypeNative +} + +type ExtBidderUcfunnel struct { + Bidder openrtb_ext.ExtImpUcfunnel `json:"bidder"` +} diff --git a/adapters/ucfunnel/ucfunnel_test.go b/adapters/ucfunnel/ucfunnel_test.go new file mode 100644 index 00000000000..60d796fdf99 --- /dev/null +++ b/adapters/ucfunnel/ucfunnel_test.go @@ -0,0 +1,163 @@ +package ucfunnel + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestUcfunnelAdapterNames(t *testing.T) { + adapter := NewUcfunnelBidder("http://localhost/bid") + adapterstest.VerifyStringValue(adapter.Name(), "ucfunnel", t) +} + +func TestSkipNoCookies(t *testing.T) { + adapter := NewUcfunnelBidder("http://localhost/bid") + status := adapter.SkipNoCookies() + if status != false { + t.Errorf("actual = %t expected != %t", status, false) + } +} + +func TestMakeRequests(t *testing.T) { + + imp := openrtb.Imp{ + ID: "1234", + Banner: &openrtb.Banner{}, + } + imp2 := openrtb.Imp{ + ID: "1235", + Video: &openrtb.Video{}, + } + + imp3 := openrtb.Imp{ + ID: "1236", + Audio: &openrtb.Audio{}, + } + + imp4 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + imp5 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + + internalRequest01 := openrtb.BidRequest{Imp: []openrtb.Imp{}} + internalRequest02 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + + internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`) + + adapter := NewUcfunnelBidder("http://localhost/bid") + + var testCases = []struct { + in []openrtb.BidRequest + out1 [](int) + out2 [](bool) + }{ + { + in: []openrtb.BidRequest{internalRequest01, internalRequest02, internalRequest03}, + out1: [](int){1, 1, 0}, + out2: [](bool){false, false, true}, + }, + } + + for idx := range testCases { + for i := range testCases[idx].in { + RequestData, err := adapter.MakeRequests(&testCases[idx].in[i], nil) + if ((RequestData == nil) == testCases[idx].out2[i]) && (len(err) == testCases[idx].out1[i]) { + t.Errorf("actual = %v expected = %v", len(err), testCases[idx].out1[i]) + } + } + } +} + +func TestMakeBids(t *testing.T) { + imp := openrtb.Imp{ + ID: "1234", + Banner: &openrtb.Banner{}, + } + imp2 := openrtb.Imp{ + ID: "1235", + Video: &openrtb.Video{}, + } + + imp3 := openrtb.Imp{ + ID: "1236", + Audio: &openrtb.Audio{}, + } + + imp4 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + imp5 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + + internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + internalRequest04 := openrtb.BidRequest{Imp: []openrtb.Imp{imp}} + + internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`) + internalRequest04.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "0"}}`) + + mockResponse200 := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid": [{"bid": [{"impid": "1234"}]},{"bid": [{"impid": "1235"}]},{"bid": [{"impid": "1236"}]},{"bid": [{"impid": "1237"}]}]}`)} + mockResponse203 := adapters.ResponseData{StatusCode: 203, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponse204 := adapters.ResponseData{StatusCode: 204, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponse400 := adapters.ResponseData{StatusCode: 400, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponseError := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid":[{"bid":[{"im236"}],{"bid":[{"impid":"1237}]}`)} + + RequestData01 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banner":{}},{"id":"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)} + RequestData02 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banne"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)} + + adapter := NewUcfunnelBidder("http://localhost/bid") + + var testCases = []struct { + in1 []openrtb.BidRequest + in2 []adapters.RequestData + in3 []adapters.ResponseData + out1 [](bool) + out2 [](bool) + }{ + { + in1: []openrtb.BidRequest{internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest04}, + in2: []adapters.RequestData{RequestData01, RequestData01, RequestData01, RequestData01, RequestData01, RequestData02}, + in3: []adapters.ResponseData{mockResponse200, mockResponse203, mockResponse204, mockResponse400, mockResponseError, mockResponse200}, + out1: [](bool){true, false, false, false, false, false}, + out2: [](bool){false, true, false, true, true, true}, + }, + } + + for idx := range testCases { + for i := range testCases[idx].in1 { + BidderResponse, err := adapter.MakeBids(&testCases[idx].in1[i], &testCases[idx].in2[i], &testCases[idx].in3[i]) + + if (BidderResponse == nil) == testCases[idx].out1[i] { + fmt.Println(i) + fmt.Println("BidderResponse") + t.Errorf("actual = %t expected == %v", (BidderResponse == nil), testCases[idx].out1[i]) + } + + if (err == nil) == testCases[idx].out2[i] { + fmt.Println(i) + fmt.Println("error") + t.Errorf("actual = %t expected == %v", err, testCases[idx].out2[i]) + } + } + } +} diff --git a/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json new file mode 100644 index 00000000000..2a7e4b2b861 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json @@ -0,0 +1,103 @@ +{ + "imp": [ + { + "id": "1", + "banner": { + "w": 970, + "h": 250, + "mimes": [ + "image/gif", + "image/jpeg", + "image/png", + "text/html", + "text/javascript", + "application/javascript" + ], + "pos": 1, + "battr": [ + 6, + 7 + ], + "topframe": 1 + }, + "instl": 0, + "displaymanager": "aralego.com", + "displaymanagerver": "v1.0.0", + "secure": 0, + "bidfloor": 0.01, + "bidfloorcur": "USD", + "exp": 3600, + "ext":{ + "ucfunnel":{ + "adunitid":"ad-BE7E9EB323E9996218A733887B6E924" + } + } + } + ], + "id": "c901f218-4ca6-480b-97dc-c6fd50e24544", + "at": 2, + "tmax": 300, + "bcat": [ + "IAB11-1" + ], + "badv": [ + "abc.com", + "cbn.com", + "xyz.com" + ], + "regs": { + "coppa": 0, + "ext": { + "gdpr": 0 + }, + "us_privacy": "1--" + }, + "site": { + "id": "5b88fd05beffd764bb0f7a3a", + "name": "test", + "page": "http://127.0.0.1:8000/tmp66.html", + "domain": "127.0.0.1", + "cat": [ + "IAB1" + ], + "publisher": { + "id": "par-7E6D2DB9A8922AB07B44A444D2BA67" + } + }, + "device": { + "w": 1440, + "h": 900, + "dnt": 1, + "ip": "127.0.0.1", + "js": 1, + "os": "MacOS", + "osv": "10.15.1", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36", + "language": "zh-TW", + "devicetype": 2, + "geo": { + "country": "unknown", + "type": 2 + } + }, + "user": { + "id": "e7bf9b85-9554-441c-964e-c8112d35d17b" + }, + "source": { + "fd": 1, + "ext": { + "schain": { + "complete": 1, + "ver": "1.0", + "nodes": [ + { + "asi": "aralego.com", + "sid": "par-7E6D2DB9A8922AB07B44A444D2BA67", + "rid": "c8f800c2-d285-4cb9-8fc9-f95df52f6e0c", + "hp": 1 + } + ] + } + } + } +} \ No newline at end of file diff --git a/adapters/ucfunnel/ucfunneltest/params/race/banner.json b/adapters/ucfunnel/ucfunneltest/params/race/banner.json new file mode 100644 index 00000000000..2c8c2e1e198 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "adunitid": "ad-83444226E44368D1E32E49EEBE6D29", + "partnerid": "par-2EDDB423AA24474188B843EE4842932" +} + \ No newline at end of file diff --git a/adapters/ucfunnel/ucfunneltest/params/race/video.json b/adapters/ucfunnel/ucfunneltest/params/race/video.json new file mode 100644 index 00000000000..0a562b34aa1 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/params/race/video.json @@ -0,0 +1,5 @@ +{ + "adunitid": "ad-E2B22B678D6A664E092824848D26BB2", + "partnerid": "par-2EDDB423AA24474188B843EE4842932" +} + \ No newline at end of file diff --git a/adapters/ucfunnel/usersync.go b/adapters/ucfunnel/usersync.go new file mode 100644 index 00000000000..92eba0d73e0 --- /dev/null +++ b/adapters/ucfunnel/usersync.go @@ -0,0 +1,12 @@ +package ucfunnel + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewUcfunnelSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("ucfunnel", 607, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/ucfunnel/usersync_test.go b/adapters/ucfunnel/usersync_test.go new file mode 100644 index 00000000000..45320b8cac1 --- /dev/null +++ b/adapters/ucfunnel/usersync_test.go @@ -0,0 +1,30 @@ +package ucfunnel + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestUcfunnelSyncer(t *testing.T) { + syncURL := "//sync.aralego.com/idsync?gdpr={{.GDPR}}&redirect=externalURL.com%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewUcfunnelSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "//sync.aralego.com/idsync?gdpr=0&redirect=externalURL.com%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 607, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 7c9ffa71672..b9501cf6355 100644 --- a/config/config.go +++ b/config/config.go @@ -529,6 +529,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderTappx doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUcfunnel, "https://sync.aralego.com/idsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&usprivacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. @@ -719,6 +720,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.triplelift_native.disabled", true) v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}") v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20") + v.SetDefault("adapters.ucfunnel.endpoint", "http://apac-hk-adx.aralego.com/prebid") v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2") v.SetDefault("adapters.verizonmedia.disabled", true) v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 4c6da00f337..f0ff9c5b1a7 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -55,6 +55,7 @@ import ( "github.com/prebid/prebid-server/adapters/tappx" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" + "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" @@ -123,6 +124,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderTappx: tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint), openrtb_ext.BidderTriplelift: triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint), openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo), + openrtb_ext.BidderUcfunnel: ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint), openrtb_ext.BidderUnruly: unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint), openrtb_ext.BidderVerizonMedia: verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint), openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 3ae443410b9..d1490603b50 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -68,6 +68,7 @@ const ( BidderTappx BidderName = "tappx" BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" + BidderUcfunnel BidderName = "ucfunnel" BidderUnruly BidderName = "unruly" BidderVerizonMedia BidderName = "verizonmedia" BidderVisx BidderName = "visx" @@ -125,6 +126,7 @@ var BidderMap = map[string]BidderName{ "tappx": BidderTappx, "triplelift": BidderTriplelift, "triplelift_native": BidderTripleliftNative, + "ucfunnel": BidderUcfunnel, "unruly": BidderUnruly, "verizonmedia": BidderVerizonMedia, "visx": BidderVisx, diff --git a/openrtb_ext/imp_ucfunnel.go b/openrtb_ext/imp_ucfunnel.go new file mode 100644 index 00000000000..408c1e0a35e --- /dev/null +++ b/openrtb_ext/imp_ucfunnel.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpUcfunnel defines the contract for bidrequest.imp[i].ext.ucfunnel +type ExtImpUcfunnel struct { + AdUnitId string `json:"adunitid"` + PartnerId string `json:"partnerid"` +} diff --git a/static/bidder-info/ucfunnel.yaml b/static/bidder-info/ucfunnel.yaml new file mode 100644 index 00000000000..288b0b3f1b8 --- /dev/null +++ b/static/bidder-info/ucfunnel.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "support@ucfunnel.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/ucfunnel.json b/static/bidder-params/ucfunnel.json new file mode 100644 index 00000000000..d39d006cf1f --- /dev/null +++ b/static/bidder-params/ucfunnel.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Ucfunnel Adapter Params", + "description": "A schema which validates params accepted by the Ucfunnel adapter", + "type": "object", + "properties": { + "adunitid": { + "type": "string", + "description": "ID for ad unit" + }, + "partnerid": { + "type": "string", + "description": "ID for partner" + } + }, + "required": ["partnerid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 7f65c7f476f..eb25171854a 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -48,6 +48,7 @@ import ( "github.com/prebid/prebid-server/adapters/synacormedia" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" + "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" @@ -107,6 +108,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderUcfunnel, ucfunnel.NewUcfunnelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderUnruly, unruly.NewUnrulySyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 2a8d1fd1b0b..dc224fe99bf 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -57,6 +57,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderSynacormedia): syncConfig, string(openrtb_ext.BidderTriplelift): syncConfig, string(openrtb_ext.BidderTripleliftNative): syncConfig, + string(openrtb_ext.BidderUcfunnel): syncConfig, string(openrtb_ext.BidderUnruly): syncConfig, string(openrtb_ext.BidderVerizonMedia): syncConfig, string(openrtb_ext.BidderVisx): syncConfig, From 199d1dcaecc3685596c6a31bf02631fc97bc8a37 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Tue, 10 Mar 2020 01:04:30 +0300 Subject: [PATCH 024/318] Update required params for TheMediaGrid adapter (#1188) --- adapters/grid/grid.go | 44 ++++++++++++++++++- .../gridtest/exemplary/simple-banner.json | 8 +++- .../grid/gridtest/exemplary/simple-video.json | 8 +++- .../grid/gridtest/params/race/banner.json | 5 ++- .../supplemental/bad_bidder_request.json | 33 ++++++++++++++ .../supplemental/bad_ext_request.json | 30 +++++++++++++ .../gridtest/supplemental/bad_response.json | 2 + .../supplemental/empty_uid_request.json | 33 ++++++++++++++ .../gridtest/supplemental/no_imp_request.json | 13 ++++++ .../gridtest/supplemental/status_204.json | 2 + .../gridtest/supplemental/status_400.json | 2 + .../gridtest/supplemental/status_418.json | 2 + openrtb_ext/imp_grid.go | 6 +++ static/bidder-params/grid.json | 7 ++- 14 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 adapters/grid/gridtest/supplemental/bad_bidder_request.json create mode 100644 adapters/grid/gridtest/supplemental/bad_ext_request.json create mode 100644 adapters/grid/gridtest/supplemental/empty_uid_request.json create mode 100644 adapters/grid/gridtest/supplemental/no_imp_request.json create mode 100644 openrtb_ext/imp_grid.go diff --git a/adapters/grid/grid.go b/adapters/grid/grid.go index 3e38edd4578..dd18d52d95a 100644 --- a/adapters/grid/grid.go +++ b/adapters/grid/grid.go @@ -15,11 +15,53 @@ type GridAdapter struct { endpoint string } +func processImp(imp *openrtb.Imp) error { + // get the grid extension + var ext adapters.ExtImpBidder + var gridExt openrtb_ext.ExtImpGrid + if err := json.Unmarshal(imp.Ext, &ext); err != nil { + return err + } + if err := json.Unmarshal(ext.Bidder, &gridExt); err != nil { + return err + } + + if gridExt.Uid == 0 { + err := &errortypes.BadInput{ + Message: "uid is empty", + } + return err + } + // no error + return nil +} + // MakeRequests makes the HTTP requests which should be made to fetch bids. func (a *GridAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var errors = make([]error, 0) - reqJSON, err := json.Marshal(request) + // copy the request, because we are going to mutate it + requestCopy := *request + // this will contain all the valid impressions + var validImps []openrtb.Imp + // pre-process the imps + for _, imp := range requestCopy.Imp { + if err := processImp(&imp); err == nil { + validImps = append(validImps, imp) + } else { + errors = append(errors, err) + } + } + if len(validImps) == 0 { + err := &errortypes.BadInput{ + Message: "No valid impressions for grid", + } + errors = append(errors, err) + return nil, errors + } + requestCopy.Imp = validImps + + reqJSON, err := json.Marshal(requestCopy) if err != nil { errors = append(errors, err) return nil, errors diff --git a/adapters/grid/gridtest/exemplary/simple-banner.json b/adapters/grid/gridtest/exemplary/simple-banner.json index b098a94f9ba..1a5ea014d0f 100644 --- a/adapters/grid/gridtest/exemplary/simple-banner.json +++ b/adapters/grid/gridtest/exemplary/simple-banner.json @@ -13,7 +13,9 @@ }] }, "ext": { - "bidder": {} + "bidder": { + "uid": 1 + } } }] }, @@ -35,7 +37,9 @@ }] }, "ext": { - "bidder": {} + "bidder": { + "uid": 1 + } } }] } diff --git a/adapters/grid/gridtest/exemplary/simple-video.json b/adapters/grid/gridtest/exemplary/simple-video.json index fcf783da2a4..12c3771d1b2 100644 --- a/adapters/grid/gridtest/exemplary/simple-video.json +++ b/adapters/grid/gridtest/exemplary/simple-video.json @@ -13,7 +13,9 @@ "h": 250 }, "ext": { - "bidder": {} + "bidder": { + "uid": 1 + } } }] }, @@ -35,7 +37,9 @@ "h": 250 }, "ext": { - "bidder": {} + "bidder": { + "uid": 1 + } } }] } diff --git a/adapters/grid/gridtest/params/race/banner.json b/adapters/grid/gridtest/params/race/banner.json index 0967ef424bc..7e347f11b45 100644 --- a/adapters/grid/gridtest/params/race/banner.json +++ b/adapters/grid/gridtest/params/race/banner.json @@ -1 +1,4 @@ -{} +{ + "uid": 1 +} + diff --git a/adapters/grid/gridtest/supplemental/bad_bidder_request.json b/adapters/grid/gridtest/supplemental/bad_bidder_request.json new file mode 100644 index 00000000000..347a3091a41 --- /dev/null +++ b/adapters/grid/gridtest/supplemental/bad_bidder_request.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": "some not exist" + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpGrid", + "comparison": "literal" + }, + { + "value": "No valid impressions for grid", + "comparison": "literal" + } + ], + "httpCalls":[], + "expectedBidResponses": [] +} diff --git a/adapters/grid/gridtest/supplemental/bad_ext_request.json b/adapters/grid/gridtest/supplemental/bad_ext_request.json new file mode 100644 index 00000000000..789db8504f8 --- /dev/null +++ b/adapters/grid/gridtest/supplemental/bad_ext_request.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": "any" + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + }, + { + "value": "No valid impressions for grid", + "comparison": "literal" + } + ], + "httpCalls": [] +} diff --git a/adapters/grid/gridtest/supplemental/bad_response.json b/adapters/grid/gridtest/supplemental/bad_response.json index 4ad5c09cf37..87436da7fc1 100644 --- a/adapters/grid/gridtest/supplemental/bad_response.json +++ b/adapters/grid/gridtest/supplemental/bad_response.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } @@ -39,6 +40,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } diff --git a/adapters/grid/gridtest/supplemental/empty_uid_request.json b/adapters/grid/gridtest/supplemental/empty_uid_request.json new file mode 100644 index 00000000000..ff389899788 --- /dev/null +++ b/adapters/grid/gridtest/supplemental/empty_uid_request.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "uid is empty", + "comparison": "literal" + }, + { + "value": "No valid impressions for grid", + "comparison": "literal" + } + ], + "httpCalls":[], + "expectedBidResponses": [] +} diff --git a/adapters/grid/gridtest/supplemental/no_imp_request.json b/adapters/grid/gridtest/supplemental/no_imp_request.json new file mode 100644 index 00000000000..5e261647fb5 --- /dev/null +++ b/adapters/grid/gridtest/supplemental/no_imp_request.json @@ -0,0 +1,13 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No valid impressions for grid", + "comparison": "literal" + } + ], + "httpCalls":[] +} diff --git a/adapters/grid/gridtest/supplemental/status_204.json b/adapters/grid/gridtest/supplemental/status_204.json index 906d8553bc6..f935cbe85ae 100644 --- a/adapters/grid/gridtest/supplemental/status_204.json +++ b/adapters/grid/gridtest/supplemental/status_204.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } @@ -39,6 +40,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } diff --git a/adapters/grid/gridtest/supplemental/status_400.json b/adapters/grid/gridtest/supplemental/status_400.json index dbf2a4d7b2b..629b1c07bd7 100644 --- a/adapters/grid/gridtest/supplemental/status_400.json +++ b/adapters/grid/gridtest/supplemental/status_400.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } @@ -39,6 +40,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } diff --git a/adapters/grid/gridtest/supplemental/status_418.json b/adapters/grid/gridtest/supplemental/status_418.json index 7619cd6aec1..0ca365c76ce 100644 --- a/adapters/grid/gridtest/supplemental/status_418.json +++ b/adapters/grid/gridtest/supplemental/status_418.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } @@ -39,6 +40,7 @@ }, "ext": { "bidder": { + "uid": 1 } } } diff --git a/openrtb_ext/imp_grid.go b/openrtb_ext/imp_grid.go new file mode 100644 index 00000000000..d38e610d7a5 --- /dev/null +++ b/openrtb_ext/imp_grid.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpGrid defines the contract for bidrequest.imp[i].ext.grid +type ExtImpGrid struct { + Uid int `json:"uid"` +} diff --git a/static/bidder-params/grid.json b/static/bidder-params/grid.json index 7a0cf3da8c5..67f9b12f115 100644 --- a/static/bidder-params/grid.json +++ b/static/bidder-params/grid.json @@ -3,6 +3,11 @@ "title": "TheMediaGrid Adapter Params", "description": "A schema which validates params accepted by TheMediaGrid adapter", "type": "object", - "properties": {}, + "properties": { + "uid": { + "type": "integer", + "description": "An ID which identifies this placement of the impression" + } + }, "required": [] } From 5a9da6672f5c51b4c90b8d0feb4e7649d039bf1e Mon Sep 17 00:00:00 2001 From: htang555 Date: Thu, 12 Mar 2020 10:43:41 -0700 Subject: [PATCH 025/318] add zeroclickfraud adapter (#1207) * add zeroclickfraud adapter * fixes for PR * fix casing of Zeroclickfraud --- adapters/zeroclickfraud/usersync.go | 12 ++ adapters/zeroclickfraud/usersync_test.go | 34 ++++ adapters/zeroclickfraud/zeroclickfraud.go | 187 ++++++++++++++++++ .../zeroclickfraud/zeroclickfraud_test.go | 11 ++ .../exemplary/multi-request.json | 160 +++++++++++++++ .../zeroclickfraudtest/exemplary/native.json | 123 ++++++++++++ .../exemplary/simple-banner.json | 133 +++++++++++++ .../exemplary/simple-video.json | 138 +++++++++++++ .../params/race/banner.json | 4 + .../params/race/native.json | 4 + .../zeroclickfraudtest/params/race/video.json | 4 + .../supplemental/bad-host.json | 33 ++++ .../supplemental/bad-response-body.json | 88 +++++++++ .../supplemental/bad-server-response.json | 88 +++++++++ .../supplemental/bad-sourceId.json | 35 ++++ .../supplemental/missing-ext.json | 27 +++ .../supplemental/missing-extparam.json | 30 +++ .../supplemental/no-content-response.json | 82 ++++++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_zeroclickfraud.go | 7 + static/bidder-info/zeroclickfraud.yaml | 13 ++ static/bidder-params/zeroclickfraud.json | 19 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 26 files changed, 1241 insertions(+) create mode 100644 adapters/zeroclickfraud/usersync.go create mode 100644 adapters/zeroclickfraud/usersync_test.go create mode 100644 adapters/zeroclickfraud/zeroclickfraud.go create mode 100644 adapters/zeroclickfraud/zeroclickfraud_test.go create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/exemplary/multi-request.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/exemplary/native.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-banner.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-video.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/params/race/banner.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/params/race/native.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/params/race/video.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-host.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-response-body.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-server-response.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-sourceId.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-ext.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-extparam.json create mode 100644 adapters/zeroclickfraud/zeroclickfraudtest/supplemental/no-content-response.json create mode 100644 openrtb_ext/imp_zeroclickfraud.go create mode 100644 static/bidder-info/zeroclickfraud.yaml create mode 100644 static/bidder-params/zeroclickfraud.json diff --git a/adapters/zeroclickfraud/usersync.go b/adapters/zeroclickfraud/usersync.go new file mode 100644 index 00000000000..833524e4b3e --- /dev/null +++ b/adapters/zeroclickfraud/usersync.go @@ -0,0 +1,12 @@ +package zeroclickfraud + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewZeroClickFraudSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("zeroclickfraud", 0, temp, adapters.SyncTypeIframe) +} diff --git a/adapters/zeroclickfraud/usersync_test.go b/adapters/zeroclickfraud/usersync_test.go new file mode 100644 index 00000000000..30ade771a4c --- /dev/null +++ b/adapters/zeroclickfraud/usersync_test.go @@ -0,0 +1,34 @@ +package zeroclickfraud + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestZeroClickFraudSyncer(t *testing.T) { + syncURL := "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewZeroClickFraudSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + }, + CCPA: ccpa.Policy{ + Value: "1NYN", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://s.0cf.io/sync?gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24%7Buid%7D", syncInfo.URL) + assert.Equal(t, "iframe", syncInfo.Type) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/adapters/zeroclickfraud/zeroclickfraud.go b/adapters/zeroclickfraud/zeroclickfraud.go new file mode 100644 index 00000000000..963074bf637 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraud.go @@ -0,0 +1,187 @@ +package zeroclickfraud + +import ( + "encoding/json" + "fmt" + "github.com/golang/glog" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "strconv" + "text/template" +) + +type ZeroClickFraudAdapter struct { + EndpointTemplate template.Template +} + +func (a *ZeroClickFraudAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + errs := make([]error, 0, len(request.Imp)) + headers := http.Header{ + "Content-Type": {"application/json"}, + "Accept": {"application/json"}, + } + + // Pull the host and source ID info from the bidder params. + reqImps, err := splitImpressions(request.Imp) + + if err != nil { + errs = append(errs, err) + } + + requests := []*adapters.RequestData{} + + for reqExt, reqImp := range reqImps { + request.Imp = reqImp + reqJson, err := json.Marshal(request) + + if err != nil { + errs = append(errs, err) + continue + } + + urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)} + url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams) + + if err != nil { + errs = append(errs, err) + continue + } + + request := adapters.RequestData{ + Method: "POST", + Uri: url, + Body: reqJson, + Headers: headers} + + requests = append(requests, &request) + } + + return requests, errs +} + +/* +internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests) +*/ +func (a *ZeroClickFraudAdapter) MakeBids( + internalRequest *openrtb.BidRequest, + externalRequest *adapters.RequestData, + response *adapters.ResponseData, +) (*adapters.BidderResponse, []error) { + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("ERR, bad input %d", response.StatusCode), + }} + } else if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponse() + bidResponse.Currency = bidResp.Cur + + for _, seatBid := range bidResp.SeatBid { + for i := 0; i < len(seatBid.Bid); i++ { + bid := seatBid.Bid[i] + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: getMediaType(bid.ImpID, internalRequest.Imp), + }) + } + } + + return bidResponse, nil +} + +func splitImpressions(imps []openrtb.Imp) (map[openrtb_ext.ExtImpZeroClickFraud][]openrtb.Imp, error) { + + var m = make(map[openrtb_ext.ExtImpZeroClickFraud][]openrtb.Imp) + + for _, imp := range imps { + bidderParams, err := getBidderParams(&imp) + if err != nil { + return nil, err + } + + m[*bidderParams] = append(m[*bidderParams], imp) + } + + return m, nil +} + +func getBidderParams(imp *openrtb.Imp) (*openrtb_ext.ExtImpZeroClickFraud, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()), + } + } + var zeroclickfraudExt openrtb_ext.ExtImpZeroClickFraud + if err := json.Unmarshal(bidderExt.Bidder, &zeroclickfraudExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()), + } + } + + if zeroclickfraudExt.SourceId < 1 { + return nil, &errortypes.BadInput{ + Message: "Invalid/Missing SourceId", + } + } + + if len(zeroclickfraudExt.Host) < 1 { + return nil, &errortypes.BadInput{ + Message: "Invalid/Missing Host", + } + } + + return &zeroclickfraudExt, nil +} + +func getMediaType(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + + bidType := openrtb_ext.BidTypeBanner + + for _, imp := range imps { + if imp.ID == impID { + if imp.Video != nil { + bidType = openrtb_ext.BidTypeVideo + break + } else if imp.Native != nil { + bidType = openrtb_ext.BidTypeNative + break + } else { + bidType = openrtb_ext.BidTypeBanner + break + } + } + } + + return bidType +} + +func NewZeroClickFraudBidder(endpoint string) *ZeroClickFraudAdapter { + template, err := template.New("endpointTemplate").Parse(endpoint) + if err != nil { + glog.Fatal("Unable to parse endpoint url template") + return nil + } + + return &ZeroClickFraudAdapter{EndpointTemplate: *template} +} diff --git a/adapters/zeroclickfraud/zeroclickfraud_test.go b/adapters/zeroclickfraud/zeroclickfraud_test.go new file mode 100644 index 00000000000..ebe41c19d2e --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraud_test.go @@ -0,0 +1,11 @@ +package zeroclickfraud + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "zeroclickfraudtest", NewZeroClickFraudBidder("http://{{.Host}}/openrtb2?sid={{.SourceId}}")) +} diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/multi-request.json b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/multi-request.json new file mode 100644 index 00000000000..70bfb9645c8 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/multi-request.json @@ -0,0 +1,160 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + },{ + "id": "some-impression-id2", + "banner": + { + "format": [{ + "w": 300, + "h": 600 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=906295", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + },{ + "id": "some-impression-id2", + "banner": + { + "format": [ + { + "w": 300, + "h": 600 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 200, + "body": + { + "id": "some-request-id", + "bidid": "183975330-5-29038-2", + "seatbid": [ + { + "seat": "906295", + "bid": [ + { + "id": "2181314349", + "impid": "some-impression-id", + "adm": "
Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.
www.zeroclickfraud.com
\"\"", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "w": 300, + "h": 250 + }] + }], + "cur": "USD", + "ext": + {} + } + } + }], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": + { + "id": "2181314349", + "impid": "some-impression-id", + "adm": "
Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.
www.zeroclickfraud.com
\"\"", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/native.json b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/native.json new file mode 100644 index 00000000000..dcf9064f29d --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/native.json @@ -0,0 +1,123 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "native": + { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":500}},{\"id\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":2,\"data\":{\"type\":1,\"len\":200}},{\"id\":3,\"data\":{\"type\":2,\"len\":15000}},{\"id\":4,\"data\":{\"type\":6,\"len\":40}}]}", + "ver": "1.1" + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "user": + { + "buyeruid": "4610943261" + }, + "at": 1, + "tmax": 500 + }, + "httpcalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=906295", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "native": + { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":0,\"required\":1,\"title\":{\"len\":500}},{\"id\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":2,\"data\":{\"type\":1,\"len\":200}},{\"id\":3,\"data\":{\"type\":2,\"len\":15000}},{\"id\":4,\"data\":{\"type\":6,\"len\":40}}]}", + "ver": "1.1" + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "user": + { + "buyeruid": "4610943261" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status":200, + "body": { + "id": "some-request-id", + "bidid": "183975330-3-29038-2", + "seatbid": [ + { + "seat": "906295", + "bid": [ + { + "id": "2181314346", + "impid": "some-impression-id", + "adm": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{ \"id\":0,\"required\":1,\"title\":{\"text\":\"Datablocks Inc.\"}},{ \"id\":3,\"required\":0,\"data\":{\"value\":\"Datablocks provides world class \\\"Software as a Service\\\" (SaaS) solutions to its clients.\"}}],\"link\":{\"url\":\"https://t.0cf.io/c/267237/?fcid=43154325321\"},\"imptrackers\":[\"https://t.0cf.io/i/267237/?fcid=43154325321&pixel=1\"],\"jstracker\":[]}}", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "ext": {} + }] + }], + "cur": "USD", + "ext": {} + } + } + }], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2181314346", + "impid": "some-impression-id", + "adm": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{ \"id\":0,\"required\":1,\"title\":{\"text\":\"Datablocks Inc.\"}},{ \"id\":3,\"required\":0,\"data\":{\"value\":\"Datablocks provides world class \\\"Software as a Service\\\" (SaaS) solutions to its clients.\"}}],\"link\":{\"url\":\"https://t.0cf.io/c/267237/?fcid=43154325321\"},\"imptrackers\":[\"https://t.0cf.io/i/267237/?fcid=43154325321&pixel=1\"],\"jstracker\":[]}}", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "ext": {} + }, + "type":"native" + } + ] + }] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-banner.json b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-banner.json new file mode 100644 index 00000000000..1d5ee3b3a52 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-banner.json @@ -0,0 +1,133 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=906295", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 200, + "body": + { + "id": "some-request-id", + "bidid": "183975330-5-29038-2", + "seatbid": [ + { + "seat": "906295", + "bid": [ + { + "id": "2181314349", + "impid": "some-impression-id", + "adm": "
Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.
www.zeroclickfraud.com.net
\"\"", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "w": 300, + "h": 250 + }] + }], + "cur": "USD", + "ext": + {} + } + } + }], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": + { + "id": "2181314349", + "impid": "some-impression-id", + "adm": "
Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.
www.zeroclickfraud.com.net
\"\"", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906299", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-video.json b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-video.json new file mode 100644 index 00000000000..949e74602dd --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/exemplary/simple-video.json @@ -0,0 +1,138 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": + { + "mimes":[ + "video/x-flv" + ], + "w": 500, + "h": 400, + "minduration": 30 + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=906295", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": + { + "mimes":[ + "video/x-flv" + ], + "w": 500, + "h": 400, + "minduration": 30 + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 906295 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 200, + "body": + { + "id": "some-request-id", + "bidid": "183975330-4-29038-2", + "seatbid": [ + { + "seat": "906295", + "bid": [ + { + "id": "2181314347", + "impid": "some-impression-id", + "nurl": "https://t.0cf.io/wm/267237/?fcid=2181314347", + "price": 13.37, + "cid": "906293", + "adid": "906297", + "crid": "906729", + "w": 500, + "h": 400, + "ext": + { + "type": "CPM" + } + }] + }], + "cur": "USD", + "ext": + {} + } + } + }], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": + { + "id": "2181314347", + "impid": "some-impression-id", + "price": 13.37, + "nurl": "https://t.0cf.io/wm/267237/?fcid=2181314347", + "adid": "906297", + "cid": "906293", + "crid": "906729", + "w": 500, + "h": 400, + "ext": + { + "type": "CPM" + } + }, + "type": "video" + }] + }] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/params/race/banner.json b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/banner.json new file mode 100644 index 00000000000..cff0af83143 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/banner.json @@ -0,0 +1,4 @@ +{ + "sourceId": 906295, + "host": "q.0cf.io" +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/params/race/native.json b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/native.json new file mode 100644 index 00000000000..cff0af83143 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/native.json @@ -0,0 +1,4 @@ +{ + "sourceId": 906295, + "host": "q.0cf.io" +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/params/race/video.json b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/video.json new file mode 100644 index 00000000000..cff0af83143 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "sourceId": 906295, + "host": "q.0cf.io" +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-host.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-host.json new file mode 100644 index 00000000000..cee5efbe760 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-host.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "bad-host-test", + "imp": [ + { + "id": "bad-host-test-imp", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": + { + "bidder": + { + "host": "", + "sourceId": 123 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Invalid/Missing Host", + "comparison": "literal" + } + ] +} diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-response-body.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-response-body.json new file mode 100644 index 00000000000..84d6bd9d889 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-response-body.json @@ -0,0 +1,88 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=123", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 200, + "body":"foobar" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-server-response.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-server-response.json new file mode 100644 index 00000000000..fdea4f109a7 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-server-response.json @@ -0,0 +1,88 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=123", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 500, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "ERR, response with status 500", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-sourceId.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-sourceId.json new file mode 100644 index 00000000000..4d86c32cd58 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/bad-sourceId.json @@ -0,0 +1,35 @@ +{ + "mockBidRequest": { + "id": "bad-sourceId-test", + "imp": [ + { + "id": "bad-sourceId-test-imp", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 0 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Invalid/Missing SourceId", + "comparison": "literal" + } + ] + + +} diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-ext.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-ext.json new file mode 100644 index 00000000000..68d29e880b9 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-ext.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "missing-extbid-test", + "imp": [ + { + "id": "missing-extbid-test", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Missing bidder ext: unexpected end of JSON input", + "comparison": "literal" + } + ] + + +} diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-extparam.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-extparam.json new file mode 100644 index 00000000000..d272cd5347c --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/missing-extparam.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "missing-extbid-test", + "imp": [ + { + "id": "missing-extbid-test", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "sourceId":54326 + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Cannot Resolve host or sourceId: unexpected end of JSON input", + "comparison": "literal" + } + ] + + +} diff --git a/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/no-content-response.json b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/no-content-response.json new file mode 100644 index 00000000000..3a36d6e04b2 --- /dev/null +++ b/adapters/zeroclickfraud/zeroclickfraudtest/supplemental/no-content-response.json @@ -0,0 +1,82 @@ +{ + "mockBidRequest": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": + { + "uri": "http://q.0cf.io/openrtb2?sid=123", + "body": + { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "banner": + { + "format": [ + { + "w": 300, + "h": 250 + }] + }, + "ext": + { + "bidder": + { + "host": "q.0cf.io", + "sourceId": 123 + } + } + }], + "site": + { + "page": "prebid.org" + }, + "device": + { + "ip": "8.8.8.10" + }, + "at": 1, + "tmax": 500 + } + }, + "mockResponse": + { + "status": 204 + } + }], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index b9501cf6355..953854bf8de 100644 --- a/config/config.go +++ b/config/config.go @@ -534,6 +534,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderZeroClickFraud, "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") } func setDefaultUsersync(m map[string]Adapter, bidder openrtb_ext.BidderName, defaultValue string) { @@ -726,6 +727,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804") v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server") + v.SetDefault("adapters.zeroclickfraud.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("max_request_size", 1024*256) v.SetDefault("analytics.file.filename", "") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index f0ff9c5b1a7..0354f258158 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -61,6 +61,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/yieldmo" + "github.com/prebid/prebid-server/adapters/zeroclickfraud" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -130,6 +131,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), openrtb_ext.BidderVrtcal: vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint), openrtb_ext.BidderYieldmo: yieldmo.NewYieldmoBidder(cfg.Adapters[string(openrtb_ext.BidderYieldmo)].Endpoint), + openrtb_ext.BidderZeroClickFraud: zeroclickfraud.NewZeroClickFraudBidder(cfg.Adapters[string(openrtb_ext.BidderZeroClickFraud)].Endpoint), } legacyBidders := map[openrtb_ext.BidderName]adapters.Adapter{ diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index d1490603b50..ed3d20e06ab 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -74,6 +74,7 @@ const ( BidderVisx BidderName = "visx" BidderVrtcal BidderName = "vrtcal" BidderYieldmo BidderName = "yieldmo" + BidderZeroClickFraud BidderName = "zeroclickfraud" ) // BidderMap stores all the valid OpenRTB 2.x Bidders in the project. This map *must not* be mutated. @@ -132,6 +133,7 @@ var BidderMap = map[string]BidderName{ "visx": BidderVisx, "vrtcal": BidderVrtcal, "yieldmo": BidderYieldmo, + "zeroclickfraud": BidderZeroClickFraud, } // BidderList returns the values of the BidderMap diff --git a/openrtb_ext/imp_zeroclickfraud.go b/openrtb_ext/imp_zeroclickfraud.go new file mode 100644 index 00000000000..ae82fcacd9a --- /dev/null +++ b/openrtb_ext/imp_zeroclickfraud.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpZeroClickFraud defines the contract for bidrequest.imp[i].ext.datablocks +type ExtImpZeroClickFraud struct { + SourceId int `json:"sourceId"` + Host string `json:"host"` +} diff --git a/static/bidder-info/zeroclickfraud.yaml b/static/bidder-info/zeroclickfraud.yaml new file mode 100644 index 00000000000..9bf7e780914 --- /dev/null +++ b/static/bidder-info/zeroclickfraud.yaml @@ -0,0 +1,13 @@ +maintainer: + email: "henry@datablocks.net" +capabilities: + app: + mediaTypes: + - banner + - native + - video + site: + mediaTypes: + - banner + - native + - video diff --git a/static/bidder-params/zeroclickfraud.json b/static/bidder-params/zeroclickfraud.json new file mode 100644 index 00000000000..1c5e3c633b4 --- /dev/null +++ b/static/bidder-params/zeroclickfraud.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "ZeroClickFraud Adapter Params", + "description": "A schema which validates params accepted by the ZeroClickFraud adapter", + + "type": "object", + "properties": { + "sourceId": { + "type": "integer", + "minimum": 1, + "description": "Website Source Id" + }, + "host": { + "type": "string", + "description": "Network Host to request from" + } + }, + "required": ["host", "sourceId"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index eb25171854a..c58d552844d 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -54,6 +54,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/yieldmo" + "github.com/prebid/prebid-server/adapters/zeroclickfraud" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/usersync" @@ -114,6 +115,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderZeroClickFraud, zeroclickfraud.NewZeroClickFraudSyncer) return syncers } diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index dc224fe99bf..cc6d4b5870a 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -63,6 +63,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderVisx): syncConfig, string(openrtb_ext.BidderVrtcal): syncConfig, string(openrtb_ext.BidderYieldmo): syncConfig, + string(openrtb_ext.BidderZeroClickFraud): syncConfig, }, } From 8668dfca16538d894355f67d99257a331fdaea45 Mon Sep 17 00:00:00 2001 From: vstatkevich Date: Thu, 12 Mar 2020 20:44:04 +0300 Subject: [PATCH 026/318] Fix Adform's parameters regex (#1214) * 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 * Create readme file for adform * Fix Adform's parameters regex Motivation: catastrophic backtracking during regex execution Details: - https://regex101.com/r/NNQrWq/1 - string to check "url_domain:keskustelu.suomi24.fi,url_path:/matkailu/matkakohteet/aasia,layout:lg,categories:Matkailu,main_category:Matkailu" Co-authored-by: v.statkevich Co-authored-by: Olga Linkevich --- static/bidder-params/adform.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/bidder-params/adform.json b/static/bidder-params/adform.json index 308ae3e9414..67f09623ee4 100644 --- a/static/bidder-params/adform.json +++ b/static/bidder-params/adform.json @@ -16,7 +16,7 @@ "mkv": { "type": "string", "description": "Comma-separated key-value pairs. Forbidden symbols: &. Example: mkv='color:blue,length:350'", - "pattern": "^(\\s*|(([^,:&]*[^,:&\\s]+[^,:&]*)+:[^,:&]*,)*(([^,:&]*[^,:&\\s]+[^,:&]*)+:[^,:&]*,?))$" + "pattern": "^(\\s*|((\\s*[^,:&\\s]+\\s*:[^,:&]*)(,\\s*[^,:&\\s]+\\s*:[^,:&]*)*))$" }, "mkw": { "type": "string", From c515816e970fe820cbcf6ce665f67befd3bf7529 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Thu, 12 Mar 2020 11:18:24 -0700 Subject: [PATCH 027/318] If Device.UA is not present in request body, init it with user-agent from header (#1219) * If Device.UA is not present in request body, init it with user-agent from request header if it's present * Moved User-Agent handler to parseVideoRequest func and added unit test * Minor clean up Co-authored-by: Veronika Solovei --- ...o_valid_sample_with_device_user_agent.json | 80 +++++++++++++++++++ ...alid_sample_without_device_user_agent.json | 63 +++++++++++++++ endpoints/openrtb2/video_auction.go | 9 ++- endpoints/openrtb2/video_auction_test.go | 75 +++++++++++++++++ 4 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json create mode 100644 endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json new file mode 100644 index 00000000000..68c3f4e1c15 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json @@ -0,0 +1,80 @@ + +{ + "accountid": "555888777", + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [ + { + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "user": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + }, + "gdpr": { + "consentrequired": false, + "consentstring": "something" + }, + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling" + }, + "device": { + "ua": "TestHeaderSample", + "ip": "123.145.167.10", + "devicetype": 1, + "dnt": 33, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory":{ + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2,3,5,6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 + }, + "cacheconfig": { + "ttl": 42 + } +} diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json new file mode 100644 index 00000000000..e040a5625ba --- /dev/null +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json @@ -0,0 +1,63 @@ + +{ + "accountid": "555888777", + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [ + { + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "user": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + }, + "gdpr": { + "consentrequired": false, + "consentstring": "something" + }, + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling" + }, + "includebrandcategory":{ + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2,3,5,6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 + }, + "cacheconfig": { + "ttl": 42 + } +} diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index feb8de193e7..2a8663959a6 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -120,7 +120,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } } //unmarshal and validate combined result - videoBidReq, errL, podErrors := deps.parseVideoRequest(resolvedRequest) + videoBidReq, errL, podErrors := deps.parseVideoRequest(resolvedRequest, r.Header) if len(errL) > 0 { handleError(&labels, w, errL, &vo) return @@ -556,7 +556,7 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro return reqJSON, nil } -func (deps *endpointDeps) parseVideoRequest(request []byte) (req *openrtb_ext.BidRequestVideo, errs []error, podErrors []PodError) { +func (deps *endpointDeps) parseVideoRequest(request []byte, headers http.Header) (req *openrtb_ext.BidRequestVideo, errs []error, podErrors []PodError) { req = &openrtb_ext.BidRequestVideo{} if err := json.Unmarshal(request, &req); err != nil { @@ -564,6 +564,11 @@ func (deps *endpointDeps) parseVideoRequest(request []byte) (req *openrtb_ext.Bi return } + //if Device.UA is not present in request body, init it with user-agent from request header if it's present + if req.Device.UA == "" { + req.Device.UA = headers.Get("User-Agent") + } + errL, podErrors := deps.validateVideoRequest(req) if len(errL) > 0 { errs = append(errs, errL...) diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index dfe2a6a50b8..a5ad62c9fa8 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "io/ioutil" + "net/http" "net/http/httptest" "strings" "testing" @@ -745,6 +746,80 @@ func TestHandleErrorMetrics(t *testing.T) { assert.Equal(t, "request missing required field: PodConfig.Pods", mod.videoObjects[0].Errors[1].Error(), "Second error in AnalyticsObject should have message regarding Pods") } +func TestParseVideoRequestWithUserAgentAndHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_with_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + headers := http.Header{} + headers.Add("User-Agent", "TestHeader") + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + +func TestParseVideoRequestWithUserAgentAndEmptyHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_with_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + headers := http.Header{} + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + +func TestParseVideoRequestWithoutUserAgentWithHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + headers := http.Header{} + headers.Add("User-Agent", "TestHeader") + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, "TestHeader", req.Device.UA, "Device.ua should be taken from request header") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + +func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + headers := http.Header{} + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, "", req.Device.UA, "Device.ua should be empty") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} From f3787be596f55e546f4656d526b1045dced2c614 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Fri, 13 Mar 2020 14:30:34 -0700 Subject: [PATCH 028/318] Queued request timeout (#1217) Co-authored-by: Veronika Solovei --- config/config.go | 10 ++ router/aspects/request_timeout_handler.go | 43 ++++++ .../aspects/request_timeout_handler_test.go | 124 ++++++++++++++++++ router/router.go | 6 + 4 files changed, 183 insertions(+) create mode 100644 router/aspects/request_timeout_handler.go create mode 100644 router/aspects/request_timeout_handler_test.go diff --git a/config/config.go b/config/config.go index 953854bf8de..e3b6c67b651 100644 --- a/config/config.go +++ b/config/config.go @@ -64,6 +64,8 @@ type Configuration struct { AccountRequired bool `mapstructure:"account_required"` // Local private file containing SSL certificates PemCertsFile string `mapstructure:"certificates_file"` + // Custom headers to handle request timeouts from queueing infrastructure + RequestTimeoutHeaders RequestTimeoutHeaders `mapstructure:"request_timeout_headers"` } const MIN_COOKIE_SIZE_BYTES = 500 @@ -199,6 +201,11 @@ type HostCookie struct { TTL int64 `mapstructure:"ttl_days"` } +type RequestTimeoutHeaders struct { + RequestTimeInQueue string `mapstructure:"request_time_in_queue"` + RequestTimeoutInQueue string `mapstructure:"request_timeout_in_queue"` +} + func (cfg *HostCookie) TTLDuration() time.Duration { return time.Duration(cfg.TTL) * time.Hour * 24 } @@ -748,6 +755,9 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("account_required", false) v.SetDefault("certificates_file", "") + v.SetDefault("request_timeout_headers.request_time_in_queue", "") + v.SetDefault("request_timeout_headers.request_timeout_in_queue", "") + // Set environment variable support: v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.SetEnvPrefix("PBS") diff --git a/router/aspects/request_timeout_handler.go b/router/aspects/request_timeout_handler.go new file mode 100644 index 00000000000..ae11f8c5614 --- /dev/null +++ b/router/aspects/request_timeout_handler.go @@ -0,0 +1,43 @@ +package aspects + +import ( + "github.com/julienschmidt/httprouter" + "github.com/prebid/prebid-server/config" + "net/http" + "strconv" +) + +func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders) httprouter.Handle { + + return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + + reqTimeInQueue := r.Header.Get(reqTimeoutHeaders.RequestTimeInQueue) + reqTimeout := r.Header.Get(reqTimeoutHeaders.RequestTimeoutInQueue) + + //If request timeout headers are not specified - process request as usual + if reqTimeInQueue == "" || reqTimeout == "" { + f(w, r, params) + return + } + + reqTimeFloat, reqTimeFloatErr := strconv.ParseFloat(reqTimeInQueue, 64) + reqTimeoutFloat, reqTimeoutFloatErr := strconv.ParseFloat(reqTimeout, 64) + + //Return HTTP 500 if request timeout headers are incorrect (wrong format) + if reqTimeFloatErr != nil || reqTimeoutFloatErr != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("Request timeout headers are incorrect (wrong format)")) + return + } + + //Return HTTP 408 if requests stays too long in queue + if reqTimeFloat >= reqTimeoutFloat { + w.WriteHeader(http.StatusRequestTimeout) + w.Write([]byte("Queued request processing time exceeded maximum")) + return + } + + f(w, r, params) + } + +} diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go new file mode 100644 index 00000000000..b6e10fd64bf --- /dev/null +++ b/router/aspects/request_timeout_handler_test.go @@ -0,0 +1,124 @@ +package aspects + +import ( + "github.com/julienschmidt/httprouter" + "github.com/prebid/prebid-server/config" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +const reqTimeInQueueHeaderName = "X-Ngx-Request-Time" +const reqTimeoutHeaderName = "X-Request-Timeout" + +func TestAny(t *testing.T) { + testCases := []struct { + reqTimeInQueue string + reqTimeOut string + setHeaders bool + extectedRespCode int + expectedRespCodeMessage string + expectedRespBody string + expectedRespBodyMessage string + }{ + { + //TestQueuedRequestTimeoutWithTimeout + reqTimeInQueue: "6", + reqTimeOut: "5", + setHeaders: true, + extectedRespCode: http.StatusRequestTimeout, + expectedRespCodeMessage: "Http response code is incorrect, should be 408", + expectedRespBody: "Queued request processing time exceeded maximum", + expectedRespBodyMessage: "Body should have error message", + }, + { + //TestQueuedRequestTimeoutNoTimeout + reqTimeInQueue: "0.9", + reqTimeOut: "5", + setHeaders: true, + extectedRespCode: http.StatusOK, + expectedRespCodeMessage: "Http response code is incorrect, should be 200", + expectedRespBody: "Executed", + expectedRespBodyMessage: "Body should be present in response", + }, + { + //TestQueuedRequestNoHeaders + reqTimeInQueue: "", + reqTimeOut: "", + setHeaders: false, + extectedRespCode: http.StatusOK, + expectedRespCodeMessage: "Http response code is incorrect, should be 200", + expectedRespBody: "Executed", + expectedRespBodyMessage: "Body should be present in response", + }, + { + //TestQueuedRequestSomeHeaders + reqTimeInQueue: "2", + reqTimeOut: "", + setHeaders: true, + extectedRespCode: http.StatusOK, + expectedRespCodeMessage: "Http response code is incorrect, should be 200", + expectedRespBody: "Executed", + expectedRespBodyMessage: "Body should be present in response", + }, + { + //TestQueuedRequestAllHeadersIncorrect + reqTimeInQueue: "test1", + reqTimeOut: "test2", + setHeaders: true, + extectedRespCode: http.StatusInternalServerError, + expectedRespCodeMessage: "Http response code is incorrect, should be 400", + expectedRespBody: "Request timeout headers are incorrect (wrong format)", + expectedRespBodyMessage: "Body should have error message", + }, + { + //TestQueuedRequestSomeHeadersIncorrect + reqTimeInQueue: "test1", + reqTimeOut: "123", + setHeaders: true, + extectedRespCode: http.StatusInternalServerError, + expectedRespCodeMessage: "Http response code is incorrect, should be 400", + expectedRespBody: "Request timeout headers are incorrect (wrong format)", + expectedRespBodyMessage: "Body should have error message", + }, + } + + for _, test := range testCases { + result := ExecuteAspectRequest(t, test.reqTimeInQueue, test.reqTimeOut, test.setHeaders) + assert.Equal(t, test.extectedRespCode, result.Code, test.expectedRespCodeMessage) + assert.Equal(t, test.expectedRespBody, string(result.Body.Bytes()), test.expectedRespBodyMessage) + } +} + +func MockEndpoint() httprouter.Handle { + return httprouter.Handle(MockHandler) +} + +func MockHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + w.Write([]byte("Executed")) +} + +func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, setHeaders bool) *httptest.ResponseRecorder { + rw := httptest.NewRecorder() + req, err := http.NewRequest("POST", "/test", nil) + if err != nil { + assert.Fail(t, "Unable create mock http request") + } + if setHeaders { + req.Header.Set(reqTimeInQueueHeaderName, timeInQueue) + req.Header.Set(reqTimeoutHeaderName, reqTimeout) + } + + customHeaders := config.RequestTimeoutHeaders{reqTimeInQueueHeaderName, reqTimeoutHeaderName} + + handler := QueuedRequestTimeout(MockEndpoint(), customHeaders) + + r := httprouter.New() + r.POST("/test", handler) + + r.ServeHTTP(rw, req) + + return rw +} diff --git a/router/router.go b/router/router.go index 449ab65a448..7e713ca637a 100644 --- a/router/router.go +++ b/router/router.go @@ -38,6 +38,7 @@ import ( "github.com/prebid/prebid-server/pbs" metricsConf "github.com/prebid/prebid-server/pbsmetrics/config" pbc "github.com/prebid/prebid-server/prebid_cache_client" + "github.com/prebid/prebid-server/router/aspects" "github.com/prebid/prebid-server/ssl" storedRequestsConf "github.com/prebid/prebid-server/stored_requests/config" "github.com/prebid/prebid-server/usersync/usersyncers" @@ -255,6 +256,11 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r glog.Fatalf("Failed to create the video endpoint handler. %v", err) } + requestTimeoutHeaders := config.RequestTimeoutHeaders{} + if cfg.RequestTimeoutHeaders != requestTimeoutHeaders { + videoEndpoint = aspects.QueuedRequestTimeout(videoEndpoint, cfg.RequestTimeoutHeaders) + } + r.POST("/auction", endpoints.Auction(cfg, syncers, gdprPerms, r.MetricsEngine, dataCache, exchanges)) r.POST("/openrtb2/auction", openrtbEndpoint) r.POST("/openrtb2/video", videoEndpoint) From e94ca8b7e635d599a6e30fb73cc776d704afbf24 Mon Sep 17 00:00:00 2001 From: bretg Date: Mon, 16 Mar 2020 12:53:09 -0400 Subject: [PATCH 029/318] docs: adding currency support section (#1199) --- docs/endpoints/openrtb2/auction.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index 9ae6ec78bee..d670b092174 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -403,6 +403,29 @@ Example: PBS receiving a request for an interstitial imp and these parameters set, it will rewrite the format object within the interstitial imp. If the format array's first object is a size, PBS will take it as the max size for the interstitial. If that size is 1x1, it will look up the device's size and use that as the max size. If the format is not present, it will also use the device size as the max size. (1x1 support so that you don't have to omit the format object to use the device size) PBS with interstitial support will come preconfigured with a list of common ad sizes. Preferentially organized by weighing the larger and more common sizes first. But no guarantees to the ordering will be made. PBS will generate a new format list for the interstitial imp by traversing this list and picking the first 10 sizes that fall within the imp's max size and minimum percentage size. There will be no attempt to favor aspect ratios closer to the original size's aspect ratio. The limit of 10 is enforced to ensure we don't overload bidders with an overlong list. All the interstitial parameters will still be passed to the bidders, so they may recognize them and use their own size matching algorithms if they prefer. +#### Currency Support + +To set the desired 'ad server currency', use the standard OpenRTB `cur` attribute. Note that Prebid Server only looks at the first currency in the array. +``` +"cur": ["USD"] +``` + +If you want or need to define currency conversion rates (e.g. for currencies that your Prebid Server doesn't support), define ext.prebid.currency.rates. (Currently supported in PBS-Java only) + +``` +"ext": { + "prebid": { + "currency": { + "rates": { + "USD": { "UAH": 24.47, "ETB": 32.04 } + } + } + } +} +``` + +If it exists, a rate defined in ext.prebid.currency.rates has the highest priority. If a currency rate doesn't exist in the request, the external file will be used. + #### Stored Responses (PBS-Java only) While testing SDK and video integrations, it's important, but often difficult, to get consistent responses back from bidders that cover a range of scenarios like different CPM values, deals, etc. Prebid Server supports a debugging workflow in two ways: From 2bad06903f480e6117a7905f916d59f3aadafab8 Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Mon, 16 Mar 2020 23:54:59 +0700 Subject: [PATCH 030/318] Add ValueImpression Adapter (#1204) --- adapters/valueimpression/params_test.go | 52 ++++++ adapters/valueimpression/usersync.go | 12 ++ adapters/valueimpression/usersync_test.go | 35 ++++ adapters/valueimpression/valueimpression.go | 154 ++++++++++++++++++ .../valueimpression/valueimpression_test.go | 11 ++ .../exemplary/banner-and-video.json | 150 +++++++++++++++++ .../valueimpressiontest/exemplary/banner.json | 98 +++++++++++ .../valueimpressiontest/exemplary/video.json | 53 ++++++ .../supplemental/explicit-dimensions.json | 56 +++++++ .../invalid-response-no-bids.json | 50 ++++++ .../invalid-response-unmarshall-error.json | 66 ++++++++ .../supplemental/no-imps-in-request.json | 18 ++ .../supplemental/server-error-code.json | 53 ++++++ .../supplemental/server-no-content.json | 45 +++++ .../supplemental/wrong-impression-ext.json | 26 +++ .../wrong-impression-mapping.json | 75 +++++++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_valueimpression.go | 5 + static/bidder-info/valueimpression.yaml | 11 ++ static/bidder-params/valueimpression.json | 15 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 24 files changed, 994 insertions(+) create mode 100644 adapters/valueimpression/params_test.go create mode 100644 adapters/valueimpression/usersync.go create mode 100644 adapters/valueimpression/usersync_test.go create mode 100644 adapters/valueimpression/valueimpression.go create mode 100644 adapters/valueimpression/valueimpression_test.go create mode 100644 adapters/valueimpression/valueimpressiontest/exemplary/banner-and-video.json create mode 100644 adapters/valueimpression/valueimpressiontest/exemplary/banner.json create mode 100644 adapters/valueimpression/valueimpressiontest/exemplary/video.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/explicit-dimensions.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-no-bids.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-unmarshall-error.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/no-imps-in-request.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/server-error-code.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/server-no-content.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-ext.json create mode 100644 adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-mapping.json create mode 100644 openrtb_ext/imp_valueimpression.go create mode 100644 static/bidder-info/valueimpression.yaml create mode 100644 static/bidder-params/valueimpression.json diff --git a/adapters/valueimpression/params_test.go b/adapters/valueimpression/params_test.go new file mode 100644 index 00000000000..46471de24bb --- /dev/null +++ b/adapters/valueimpression/params_test.go @@ -0,0 +1,52 @@ +package valueimpression + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/valueimpression.json +// These also validate the format of the external API: request.imp[i].ext.valueimpression +// TestValidParams makes sure that the ValueImpression schema accepts all imp.ext fields which we intend to support. + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderValueImpression, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected ValueImpression params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the ValueImpression schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderValueImpression, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"siteId": "123"}`, +} + +var invalidParams = []string{ + `{}`, + `null`, + `true`, + `154`, + `{"siteId": 123}`, // siteId should be string + `{"invalid_param": "123"}`, +} diff --git a/adapters/valueimpression/usersync.go b/adapters/valueimpression/usersync.go new file mode 100644 index 00000000000..34addbc0e75 --- /dev/null +++ b/adapters/valueimpression/usersync.go @@ -0,0 +1,12 @@ +package valueimpression + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewValueImpressionSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("valueimpression", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/valueimpression/usersync_test.go b/adapters/valueimpression/usersync_test.go new file mode 100644 index 00000000000..63f123055a9 --- /dev/null +++ b/adapters/valueimpression/usersync_test.go @@ -0,0 +1,35 @@ +package valueimpression + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestValueImpressionSyncer(t *testing.T) { + syncURL := "https://rtb.valueimpression.com/usersync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewValueImpressionSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", + }, + CCPA: ccpa.Policy{ + Value: "1NYN", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://rtb.valueimpression.com/usersync?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&redirectUri=http%3A%2F%2Flocalhost:8000%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D%24UID", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.False(t, syncInfo.SupportCORS) +} diff --git a/adapters/valueimpression/valueimpression.go b/adapters/valueimpression/valueimpression.go new file mode 100644 index 00000000000..7e0f5f28cb9 --- /dev/null +++ b/adapters/valueimpression/valueimpression.go @@ -0,0 +1,154 @@ +package valueimpression + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type ValueImpressionAdapter struct { + endpoint string +} + +func (a *ValueImpressionAdapter) MakeRequests(request *openrtb.BidRequest, unused *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var adapterRequests []*adapters.RequestData + + if err := preprocess(request); err != nil { + errs = append(errs, err) + return nil, errs + } + + adapterReq, err := a.makeRequest(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + adapterRequests = append(adapterRequests, adapterReq) + + return adapterRequests, errs +} + +func (a *ValueImpressionAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, error) { + var err error + + jsonBody, err := json.Marshal(request) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + + return &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: jsonBody, + Headers: headers, + }, nil +} + +func preprocess(request *openrtb.BidRequest) error { + if len(request.Imp) == 0 { + return &errortypes.BadInput{ + Message: "No Imps in Bid Request", + } + } + for i := 0; i < len(request.Imp); i++ { + var imp = &request.Imp[i] + var bidderExt adapters.ExtImpBidder + + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + var extImp openrtb_ext.ExtImpValueImpression + if err := json.Unmarshal(bidderExt.Bidder, &extImp); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + imp.Ext = bidderExt.Bidder + } + + return nil +} + +// MakeBids based on valueimpression server response +func (a *ValueImpressionAdapter) MakeBids(bidRequest *openrtb.BidRequest, unused *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Bad user input: HTTP status %d", responseData.StatusCode), + }} + } + + if responseData.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Bad server response: HTTP status %d", responseData.StatusCode), + }} + } + + var bidResponse openrtb.BidResponse + + if err := json.Unmarshal(responseData.Body, &bidResponse); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + + if len(bidResponse.SeatBid) == 0 { + return nil, nil + } + + rv := adapters.NewBidderResponseWithBidsCapacity(len(bidResponse.SeatBid[0].Bid)) + var errors []error + + for _, seatbid := range bidResponse.SeatBid { + for _, bid := range seatbid.Bid { + foundMatchingBid := false + bidType := openrtb_ext.BidTypeBanner + for _, imp := range bidRequest.Imp { + if imp.ID == bid.ImpID { + foundMatchingBid = true + if imp.Banner != nil { + bidType = openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + bidType = openrtb_ext.BidTypeVideo + } + break + } + } + + if foundMatchingBid { + rv.Bids = append(rv.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } else { + errors = append(errors, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("bid id='%s' could not find valid impid='%s'", bid.ID, bid.ImpID), + }) + } + } + } + return rv, errors +} + +func NewValueImpressionBidder(endpoint string) *ValueImpressionAdapter { + return &ValueImpressionAdapter{ + endpoint: endpoint, + } +} diff --git a/adapters/valueimpression/valueimpression_test.go b/adapters/valueimpression/valueimpression_test.go new file mode 100644 index 00000000000..047521cea41 --- /dev/null +++ b/adapters/valueimpression/valueimpression_test.go @@ -0,0 +1,11 @@ +package valueimpression + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "valueimpressiontest", NewValueImpressionBidder("//host")) +} diff --git a/adapters/valueimpression/valueimpressiontest/exemplary/banner-and-video.json b/adapters/valueimpression/valueimpressiontest/exemplary/banner-and-video.json new file mode 100644 index 00000000000..107c0d84221 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/exemplary/banner-and-video.json @@ -0,0 +1,150 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + }, + { + "id": "test-video-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ], + "site": { + "id": "123" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "siteId": "123" + } + }, + { + "id": "test-video-imp-id", + "video": { + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 640 + }, + "ext": { + "siteId": "123" + } + } + ], + "site": { + "id": "123" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "valueimpression", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-video-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "sample.com" + ], + "cid": "958", + "crid": "29681110", + "w": 1024, + "h": 576 + }, + "type": "banner" + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-video-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29484110", + "adomain": [ + "sample.com" + ], + "cid": "958", + "crid": "29484110", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] +} \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/exemplary/banner.json b/adapters/valueimpression/valueimpressiontest/exemplary/banner.json new file mode 100644 index 00000000000..1ef11ade199 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/exemplary/banner.json @@ -0,0 +1,98 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "siteId": "123" + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "valueimpression", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-banner-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-banner-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} + \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/exemplary/video.json b/adapters/valueimpression/valueimpressiontest/exemplary/video.json new file mode 100644 index 00000000000..c6e71e7a16f --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/exemplary/video.json @@ -0,0 +1,53 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-video-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-video-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/explicit-dimensions.json b/adapters/valueimpression/valueimpressiontest/supplemental/explicit-dimensions.json new file mode 100644 index 00000000000..ee23350c9dc --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/explicit-dimensions.json @@ -0,0 +1,56 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-no-bids.json b/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-no-bids.json new file mode 100644 index 00000000000..114b27bae07 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-no-bids.json @@ -0,0 +1,50 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "w": 90, + "h": 728 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "h": 728, + "w": 90 + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [ + ], + "cur": "USD" + } + } + } + ] +} \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-unmarshall-error.json b/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-unmarshall-error.json new file mode 100644 index 00000000000..c854548b78b --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/invalid-response-unmarshall-error.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "w": 90, + "h": 728 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "some_test_ad", + "banner": { + "h": 728, + "w": 90 + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [ + { + "bid": [ + { + "id": "uuid", + "impid": "some_test_ad", + "w": "728", + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go struct field Bid(\\.seatbid\\.bid)?\\.w of type uint64", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/no-imps-in-request.json b/adapters/valueimpression/valueimpressiontest/supplemental/no-imps-in-request.json new file mode 100644 index 00000000000..274a34227cf --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/no-imps-in-request.json @@ -0,0 +1,18 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + ], + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "No Imps in Bid Request", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/server-error-code.json b/adapters/valueimpression/valueimpressiontest/supplemental/server-error-code.json new file mode 100644 index 00000000000..ea31fdc2fe9 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/server-error-code.json @@ -0,0 +1,53 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Bad server response: HTTP status 500", + "comparison": "literal" + } + ] + } diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/server-no-content.json b/adapters/valueimpression/valueimpressiontest/supplemental/server-no-content.json new file mode 100644 index 00000000000..85633201bc4 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/server-no-content.json @@ -0,0 +1,45 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "some_test_auction", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] + } diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-ext.json b/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-ext.json new file mode 100644 index 00000000000..13514ac8ab8 --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-ext.json @@ -0,0 +1,26 @@ +{ + "mockBidRequest": { + "id": "rqid", + "imp": [ + { + "id": "impid", + "video": { + "w": 100, + "h": 200 + }, + "ext": { + "bidder": { + "siteId": 123 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal number into Go struct field ExtImpValueImpression.siteId of type string", + "comparison": "literal" + } + ] +} diff --git a/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-mapping.json b/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-mapping.json new file mode 100644 index 00000000000..ef4d3a7526b --- /dev/null +++ b/adapters/valueimpression/valueimpressiontest/supplemental/wrong-impression-mapping.json @@ -0,0 +1,75 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "siteId": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "//host", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "siteId": "123" + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "BOGUS-IMPID", + "price": 3.5, + "w": 900, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "bid id='test-bid-id' could not find valid impid='BOGUS-IMPID'", + "comparison": "regex" + } +] +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index e3b6c67b651..2069730b692 100644 --- a/config/config.go +++ b/config/config.go @@ -538,6 +538,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUcfunnel, "https://sync.aralego.com/idsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&usprivacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderValueImpression, "https://rtb.valueimpression.com/usersync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -730,6 +731,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20") v.SetDefault("adapters.ucfunnel.endpoint", "http://apac-hk-adx.aralego.com/prebid") v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2") + v.SetDefault("adapters.valueimpression.endpoint", "https://rtb.valueimpression.com/endpoint") v.SetDefault("adapters.verizonmedia.disabled", true) v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 0354f258158..7b841a2838e 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -57,6 +57,7 @@ import ( "github.com/prebid/prebid-server/adapters/triplelift_native" "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" + "github.com/prebid/prebid-server/adapters/valueimpression" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" @@ -127,6 +128,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo), openrtb_ext.BidderUcfunnel: ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint), openrtb_ext.BidderUnruly: unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint), + openrtb_ext.BidderValueImpression: valueimpression.NewValueImpressionBidder(cfg.Adapters[string(openrtb_ext.BidderValueImpression)].Endpoint), openrtb_ext.BidderVerizonMedia: verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint), openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), openrtb_ext.BidderVrtcal: vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index ed3d20e06ab..627842f57ff 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -70,6 +70,7 @@ const ( BidderTripleliftNative BidderName = "triplelift_native" BidderUcfunnel BidderName = "ucfunnel" BidderUnruly BidderName = "unruly" + BidderValueImpression BidderName = "valueimpression" BidderVerizonMedia BidderName = "verizonmedia" BidderVisx BidderName = "visx" BidderVrtcal BidderName = "vrtcal" @@ -129,6 +130,7 @@ var BidderMap = map[string]BidderName{ "triplelift_native": BidderTripleliftNative, "ucfunnel": BidderUcfunnel, "unruly": BidderUnruly, + "valueimpression": BidderValueImpression, "verizonmedia": BidderVerizonMedia, "visx": BidderVisx, "vrtcal": BidderVrtcal, diff --git a/openrtb_ext/imp_valueimpression.go b/openrtb_ext/imp_valueimpression.go new file mode 100644 index 00000000000..7c5c70ee0a7 --- /dev/null +++ b/openrtb_ext/imp_valueimpression.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpValueImpression struct { + SiteId string `json:"siteId"` +} diff --git a/static/bidder-info/valueimpression.yaml b/static/bidder-info/valueimpression.yaml new file mode 100644 index 00000000000..1d64abcb68f --- /dev/null +++ b/static/bidder-info/valueimpression.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "info@valueimpression.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/valueimpression.json b/static/bidder-params/valueimpression.json new file mode 100644 index 00000000000..5b9c32c592e --- /dev/null +++ b/static/bidder-params/valueimpression.json @@ -0,0 +1,15 @@ + +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "ValueImpression Adapter Params", + "description": "Schema to validate params accepted by the ValueImpression adapter", + + "type": "object", + "properties": { + "siteId": { + "type": "string", + "description": "Site ID" + } + }, + "required": ["siteId"] + } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index c58d552844d..c7ad70b7eff 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -50,6 +50,7 @@ import ( "github.com/prebid/prebid-server/adapters/triplelift_native" "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" + "github.com/prebid/prebid-server/adapters/valueimpression" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" @@ -111,6 +112,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderUcfunnel, ucfunnel.NewUcfunnelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderUnruly, unruly.NewUnrulySyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderValueImpression, valueimpression.NewValueImpressionSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index cc6d4b5870a..7aef9fa8b5a 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -59,6 +59,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderTripleliftNative): syncConfig, string(openrtb_ext.BidderUcfunnel): syncConfig, string(openrtb_ext.BidderUnruly): syncConfig, + string(openrtb_ext.BidderValueImpression): syncConfig, string(openrtb_ext.BidderVerizonMedia): syncConfig, string(openrtb_ext.BidderVisx): syncConfig, string(openrtb_ext.BidderVrtcal): syncConfig, From 95c269f3740c4c0e1bf82b481ca78f52e634cb17 Mon Sep 17 00:00:00 2001 From: rhaksi-kidoz <61601767+rhaksi-kidoz@users.noreply.github.com> Date: Tue, 17 Mar 2020 22:27:52 -0700 Subject: [PATCH 031/318] Kidoz adapter (#1210) Co-authored-by: Ryan Haksi --- .gitignore | 4 + adapters/kidoz/kidoz.go | 188 ++++++++++++++++++ adapters/kidoz/kidoz_test.go | 113 +++++++++++ .../kidoztest/exemplary/simple-banner.json | 71 +++++++ .../kidoztest/exemplary/simple-video.json | 69 +++++++ .../kidoz/kidoztest/supplemental/bad-bid.json | 96 +++++++++ .../supplemental/bidder-marshal.json | 30 +++ .../supplemental/empty-banner-format .json | 19 ++ .../kidoztest/supplemental/ext-marshal.json | 28 +++ .../supplemental/missing-banner-format.json | 18 ++ .../supplemental/missing-bidder.json | 25 +++ .../kidoztest/supplemental/missing-ext.json | 24 +++ .../supplemental/missing-kidoz-info.json | 52 +++++ .../supplemental/only-video-banner.json | 27 +++ .../kidoztest/supplemental/status-204.json | 57 ++++++ .../kidoztest/supplemental/status-400.json | 63 ++++++ .../kidoztest/supplemental/status-403.json | 63 ++++++ .../kidoztest/supplemental/status-408.json | 63 ++++++ .../kidoztest/supplemental/status-500.json | 63 ++++++ .../kidoztest/supplemental/status-502.json | 63 ++++++ .../kidoztest/supplemental/status-503.json | 58 ++++++ .../kidoztest/supplemental/status-504.json | 63 ++++++ adapters/kidoz/params_test.go | 79 ++++++++ analytics/config/testFiles/test-20200303 | 0 config/config.go | 1 + exchange/adapter_map.go | 5 +- go.mod | 1 + go.sum | 2 + openrtb_ext/bid.go | 6 +- openrtb_ext/bidders.go | 2 + openrtb_ext/imp_kidoz.go | 6 + static/bidder-info/kidoz.yaml | 11 + static/bidder-params/kidoz.json | 26 +++ usersync/usersyncers/syncer_test.go | 1 + validate.sh | 4 +- 35 files changed, 1394 insertions(+), 7 deletions(-) create mode 100644 adapters/kidoz/kidoz.go create mode 100644 adapters/kidoz/kidoz_test.go create mode 100644 adapters/kidoz/kidoztest/exemplary/simple-banner.json create mode 100644 adapters/kidoz/kidoztest/exemplary/simple-video.json create mode 100644 adapters/kidoz/kidoztest/supplemental/bad-bid.json create mode 100644 adapters/kidoz/kidoztest/supplemental/bidder-marshal.json create mode 100644 adapters/kidoz/kidoztest/supplemental/empty-banner-format .json create mode 100644 adapters/kidoz/kidoztest/supplemental/ext-marshal.json create mode 100644 adapters/kidoz/kidoztest/supplemental/missing-banner-format.json create mode 100644 adapters/kidoz/kidoztest/supplemental/missing-bidder.json create mode 100644 adapters/kidoz/kidoztest/supplemental/missing-ext.json create mode 100644 adapters/kidoz/kidoztest/supplemental/missing-kidoz-info.json create mode 100644 adapters/kidoz/kidoztest/supplemental/only-video-banner.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-204.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-400.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-403.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-408.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-500.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-502.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-503.json create mode 100644 adapters/kidoz/kidoztest/supplemental/status-504.json create mode 100644 adapters/kidoz/params_test.go create mode 100644 analytics/config/testFiles/test-20200303 create mode 100644 openrtb_ext/imp_kidoz.go create mode 100644 static/bidder-info/kidoz.yaml create mode 100644 static/bidder-params/kidoz.json diff --git a/.gitignore b/.gitignore index c2cbc1e97d5..60c24e79c0d 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,10 @@ debug pbs.* inventory_url.yaml +# generated log files during tests +analytics/config/testFiles/ +analytics/filesystem/testFiles/ + # autogenerated version file # static/version.txt diff --git a/adapters/kidoz/kidoz.go b/adapters/kidoz/kidoz.go new file mode 100644 index 00000000000..2d04cdffd39 --- /dev/null +++ b/adapters/kidoz/kidoz.go @@ -0,0 +1,188 @@ +package kidoz + +import ( + "encoding/json" + "errors" + "net/http" + "strconv" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type KidozAdapter struct { + endpoint string +} + +func (a *KidozAdapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + + impressions := request.Imp + result := make([]*adapters.RequestData, 0, len(impressions)) + errs := make([]error, 0, len(impressions)) + + for i, impression := range impressions { + if impression.Banner == nil && impression.Video == nil { + errs = append(errs, &errortypes.BadInput{ + Message: "Kidoz only supports banner or video ads", + }) + continue + } + + if impression.Banner != nil { + banner := impression.Banner + if banner.Format == nil { + errs = append(errs, &errortypes.BadInput{ + Message: "banner format required", + }) + continue + } + if len(banner.Format) == 0 { + errs = append(errs, &errortypes.BadInput{ + Message: "banner format array is empty", + }) + continue + } + } + + if len(impression.Ext) == 0 { + errs = append(errs, errors.New("impression extensions required")) + continue + } + var bidderExt adapters.ExtImpBidder + err := json.Unmarshal(impression.Ext, &bidderExt) + if err != nil { + errs = append(errs, err) + continue + } + if len(bidderExt.Bidder) == 0 { + errs = append(errs, errors.New("bidder required")) + continue + } + var impressionExt openrtb_ext.ExtImpKidoz + err = json.Unmarshal(bidderExt.Bidder, &impressionExt) + if err != nil { + errs = append(errs, err) + continue + } + if impressionExt.AccessToken == "" { + errs = append(errs, errors.New("Kidoz access_token required")) + continue + } + if impressionExt.PublisherID == "" { + errs = append(errs, errors.New("Kidoz publisher_id required")) + continue + } + + request.Imp = impressions[i : i+1] + body, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + continue + } + result = append(result, &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: body, + Headers: headers, + }) + } + + request.Imp = impressions + + if len(result) == 0 { + return nil, errs + } + return result, errs +} + +func (a *KidozAdapter) MakeBids(request *openrtb.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + switch responseData.StatusCode { + case http.StatusNoContent: + fallthrough + case http.StatusServiceUnavailable: + return nil, nil + + case http.StatusBadRequest: + fallthrough + case http.StatusUnauthorized: + fallthrough + case http.StatusForbidden: + return nil, []error{&errortypes.BadInput{ + Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body), + }} + + case http.StatusOK: + break + + default: + return nil, []error{&errortypes.BadServerResponse{ + Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body), + }} + } + + var bidResponse openrtb.BidResponse + err := json.Unmarshal(responseData.Body, &bidResponse) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + + response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + + for _, seatBid := range bidResponse.SeatBid { + for _, bid := range seatBid.Bid { + thisBid := bid + bidType := GetMediaTypeForImp(bid.ImpID, request.Imp) + if bidType == UndefinedMediaType { + errs = append(errs, &errortypes.BadServerResponse{ + Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID, + }) + continue + } + response.Bids = append(response.Bids, &adapters.TypedBid{ + Bid: &thisBid, + BidType: bidType, + }) + } + } + + return response, errs +} + +func NewKidozBidder(endpoint string) *KidozAdapter { + return &KidozAdapter{ + endpoint: endpoint, + } +} + +const UndefinedMediaType = openrtb_ext.BidType("") + +func GetMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + var bidType openrtb_ext.BidType = UndefinedMediaType + for _, impression := range imps { + if impression.ID != impID { + continue + } + switch { + case impression.Banner != nil: + bidType = openrtb_ext.BidTypeBanner + case impression.Video != nil: + bidType = openrtb_ext.BidTypeVideo + case impression.Native != nil: + bidType = openrtb_ext.BidTypeNative + case impression.Audio != nil: + bidType = openrtb_ext.BidTypeAudio + } + break + } + return bidType +} diff --git a/adapters/kidoz/kidoz_test.go b/adapters/kidoz/kidoz_test.go new file mode 100644 index 00000000000..55036c08614 --- /dev/null +++ b/adapters/kidoz/kidoz_test.go @@ -0,0 +1,113 @@ +package kidoz + +import ( + "math" + "net/http" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "kidoztest", NewKidozBidder("http://example.com/prebid")) +} + +func makeBidRequest() *openrtb.BidRequest { + request := &openrtb.BidRequest{ + ID: "test-req-id-0", + Imp: []openrtb.Imp{ + { + ID: "test-imp-id-0", + Banner: &openrtb.Banner{ + Format: []openrtb.Format{ + { + W: 320, + H: 50, + }, + }, + }, + Ext: []byte(`{"bidder":{"access_token":"token-0","publisher_id":"pub-0"}}`), + }, + }, + } + return request +} + +func TestMakeRequests(t *testing.T) { + kidoz := NewKidozBidder("http://example.com/prebid") + + t.Run("Handles Request marshal failure", func(t *testing.T) { + request := makeBidRequest() + request.Imp[0].BidFloor = math.Inf(1) // cant be marshalled + extra := &adapters.ExtraRequestInfo{} + reqs, errs := kidoz.MakeRequests(request, extra) + // cant assert message its different on different versions of go + assert.Equal(t, 1, len(errs)) + assert.Contains(t, errs[0].Error(), "json") + assert.Equal(t, 0, len(reqs)) + }) +} + +func TestMakeBids(t *testing.T) { + kidoz := NewKidozBidder("http://example.com/prebid") + + t.Run("Handles response marshal failure", func(t *testing.T) { + request := makeBidRequest() + requestData := &adapters.RequestData{} + responseData := &adapters.ResponseData{ + StatusCode: http.StatusOK, + } + + resp, errs := kidoz.MakeBids(request, requestData, responseData) + // cant assert message its different on different versions of go + assert.Equal(t, 1, len(errs)) + assert.Contains(t, errs[0].Error(), "JSON") + assert.Nil(t, resp) + }) +} + +func TestGetMediaTypeForImp(t *testing.T) { + imps := []openrtb.Imp{ + { + ID: "1", + Banner: &openrtb.Banner{}, + }, + { + ID: "2", + Video: &openrtb.Video{}, + }, + { + ID: "3", + Native: &openrtb.Native{}, + }, + { + ID: "4", + Audio: &openrtb.Audio{}, + }, + } + + t.Run("Bid not found is type empty string", func(t *testing.T) { + actual := GetMediaTypeForImp("ARGLE_BARGLE", imps) + assert.Equal(t, UndefinedMediaType, actual) + }) + t.Run("Can find banner type", func(t *testing.T) { + actual := GetMediaTypeForImp("1", imps) + assert.Equal(t, openrtb_ext.BidTypeBanner, actual) + }) + t.Run("Can find video type", func(t *testing.T) { + actual := GetMediaTypeForImp("2", imps) + assert.Equal(t, openrtb_ext.BidTypeVideo, actual) + }) + t.Run("Can find native type", func(t *testing.T) { + actual := GetMediaTypeForImp("3", imps) + assert.Equal(t, openrtb_ext.BidTypeNative, actual) + }) + t.Run("Can find audio type", func(t *testing.T) { + actual := GetMediaTypeForImp("4", imps) + assert.Equal(t, openrtb_ext.BidTypeAudio, actual) + }) +} diff --git a/adapters/kidoz/kidoztest/exemplary/simple-banner.json b/adapters/kidoz/kidoztest/exemplary/simple-banner.json new file mode 100644 index 00000000000..c44fdba7aeb --- /dev/null +++ b/adapters/kidoz/kidoztest/exemplary/simple-banner.json @@ -0,0 +1,71 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-response-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id-1", + "impid": "test-impression-id-1", + "price": 1 + } + ], + "seat": "kidoz" + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/exemplary/simple-video.json b/adapters/kidoz/kidoztest/exemplary/simple-video.json new file mode 100644 index 00000000000..3b682078cbe --- /dev/null +++ b/adapters/kidoz/kidoztest/exemplary/simple-video.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-response-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id-1", + "impid": "test-impression-id-1", + "price": 1 + } + ], + "seat": "kidoz" + } + ] + } + } + } + ] +} diff --git a/adapters/kidoz/kidoztest/supplemental/bad-bid.json b/adapters/kidoz/kidoztest/supplemental/bad-bid.json new file mode 100644 index 00000000000..32b8ec2cf06 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/bad-bid.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-0", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-0", + "publisher_id": "test-publisher-0" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-0", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-0", + "publisher_id": "test-publisher-0" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-response-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id-0", + "impid": "test-impression-id-0", + "price": 10 + }, + { + "id": "test-bid-id-bogus", + "impid": "test-impression-id-bogus", + "price": 11 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "test-bid-id-0", + "impid": "test-impression-id-0", + "price": 10 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [], + "expectedMakeBidsErrors": [ + { + "value": "ignoring bid id=test-bid-id-bogus, request doesn't contain any valid impression with id=test-impression-id-bogus", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/bidder-marshal.json b/adapters/kidoz/kidoztest/supplemental/bidder-marshal.json new file mode 100644 index 00000000000..8a8a5e76844 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/bidder-marshal.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-7", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": "invalid bidder" + } + } + ] + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpKidoz", + "comparison": "literal" + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/empty-banner-format .json b/adapters/kidoz/kidoztest/supplemental/empty-banner-format .json new file mode 100644 index 00000000000..18b62a4e1f4 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/empty-banner-format .json @@ -0,0 +1,19 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [] + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "banner format array is empty", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/ext-marshal.json b/adapters/kidoz/kidoztest/supplemental/ext-marshal.json new file mode 100644 index 00000000000..eaab459461a --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/ext-marshal.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-7", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": "invalid ext" + } + ] + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/missing-banner-format.json b/adapters/kidoz/kidoztest/supplemental/missing-banner-format.json new file mode 100644 index 00000000000..3fdb5443c78 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/missing-banner-format.json @@ -0,0 +1,18 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "banner format required", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/missing-bidder.json b/adapters/kidoz/kidoztest/supplemental/missing-bidder.json new file mode 100644 index 00000000000..06ab222e322 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/missing-bidder.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": {} + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "bidder required", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/missing-ext.json b/adapters/kidoz/kidoztest/supplemental/missing-ext.json new file mode 100644 index 00000000000..8424a0b173a --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/missing-ext.json @@ -0,0 +1,24 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "impression extensions required", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/missing-kidoz-info.json b/adapters/kidoz/kidoztest/supplemental/missing-kidoz-info.json new file mode 100644 index 00000000000..bfe67aa7cea --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/missing-kidoz-info.json @@ -0,0 +1,52 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-5", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-5" + } + } + }, + { + "id": "test-impression-id-6", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "publisher_id": "test-publisher-6" + } + } + } + ] + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "Kidoz publisher_id required", + "comparison": "literal" + }, + { + "value": "Kidoz access_token required", + "comparison": "literal" + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/only-video-banner.json b/adapters/kidoz/kidoztest/supplemental/only-video-banner.json new file mode 100644 index 00000000000..6e87e80806c --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/only-video-banner.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-0", + "audio": { + } + }, + { + "id": "test-impression-id-1", + "native": { + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Kidoz only supports banner or video ads", + "comparison": "literal" + }, + { + "value": "Kidoz only supports banner or video ads", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-204.json b/adapters/kidoz/kidoztest/supplemental/status-204.json new file mode 100644 index 00000000000..0bff102259a --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-204.json @@ -0,0 +1,57 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-400.json b/adapters/kidoz/kidoztest/supplemental/status-400.json new file mode 100644 index 00000000000..ca42aefdca0 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-400.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 400 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-403.json b/adapters/kidoz/kidoztest/supplemental/status-403.json new file mode 100644 index 00000000000..3b6d268ecfe --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-403.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 403, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 403 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-408.json b/adapters/kidoz/kidoztest/supplemental/status-408.json new file mode 100644 index 00000000000..8230967f3a8 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-408.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 408, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 408 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-500.json b/adapters/kidoz/kidoztest/supplemental/status-500.json new file mode 100644 index 00000000000..f734e6913a7 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-500.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 500 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-502.json b/adapters/kidoz/kidoztest/supplemental/status-502.json new file mode 100644 index 00000000000..b99f52a2e42 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-502.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 502, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 502 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-503.json b/adapters/kidoz/kidoztest/supplemental/status-503.json new file mode 100644 index 00000000000..f823372915c --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-503.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 503, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/kidoz/kidoztest/supplemental/status-504.json b/adapters/kidoz/kidoztest/supplemental/status-504.json new file mode 100644 index 00000000000..b996611eb97 --- /dev/null +++ b/adapters/kidoz/kidoztest/supplemental/status-504.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-id-1", + "banner": { + "format": [ + { + "h": 250, + "w": 300 + } + ] + }, + "ext": { + "bidder": { + "access_token": "test-token-1", + "publisher_id": "test-publisher-1" + } + } + } + ] + } + }, + "mockResponse": { + "status": 504, + "body": "server text here" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 504 \"server text here\"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kidoz/params_test.go b/adapters/kidoz/params_test.go new file mode 100644 index 00000000000..073d7382d68 --- /dev/null +++ b/adapters/kidoz/params_test.go @@ -0,0 +1,79 @@ +package kidoz + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderKidoz, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected kidoz params: %s \n Error: %s", validParam, err) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderKidoz, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"publisher_id":"pub-valid-0", "access_token":"token-valid-0"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"some_random_field":""}`, + `{"publisher_id":""}`, + `{"publisher_id": 1}`, + `{"publisher_id": 1.2}`, + `{"publisher_id": null}`, + `{"publisher_id": true}`, + `{"publisher_id": []}`, + `{"publisher_id": {}}`, + `{"publisher_id":"", "access_token":"token-valid-0"}`, + `{"publisher_id": 1, "access_token":"token-valid-0"}`, + `{"publisher_id": 1.2, "access_token":"token-valid-0"}`, + `{"publisher_id": null, "access_token":"token-valid-0"}`, + `{"publisher_id": true, "access_token":"token-valid-0"}`, + `{"publisher_id": [], "access_token":"token-valid-0"}`, + `{"publisher_id": {}, "access_token":"token-valid-0"}`, + `{"access_token":""}`, + `{"access_token": 1}`, + `{"access_token": 1.2}`, + `{"access_token": null}`, + `{"access_token": true}`, + `{"access_token": []}`, + `{"access_token": {}}`, + `{"access_token":"", "publisher_id":"pub-valid-0"}`, + `{"access_token": 1, "publisher_id":"pub-valid-0"}`, + `{"access_token": 1.2, "publisher_id":"pub-valid-0"}`, + `{"access_token": null, "publisher_id":"pub-valid-0"}`, + `{"access_token": true, "publisher_id":"pub-valid-0"}`, + `{"access_token": [], "publisher_id":"pub-valid-0"}`, + `{"access_token": {}, "publisher_id":"pub-valid-0"}`, + `{"access_token": 1, "publisher_id":"pub-valid-0"}`, + `{"access_token":"token-valid-0", "publisher_id": 1}`, +} diff --git a/analytics/config/testFiles/test-20200303 b/analytics/config/testFiles/test-20200303 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/config/config.go b/config/config.go index 2069730b692..d4edab2b53f 100644 --- a/config/config.go +++ b/config/config.go @@ -707,6 +707,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid") v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs") v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932") + v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server") v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid") v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest") v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 7b841a2838e..05f44e24b66 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -5,8 +5,6 @@ import ( "net/http" "strings" - "github.com/prebid/prebid-server/adapters/kubient" - "github.com/prebid/prebid-server/adapters" ttx "github.com/prebid/prebid-server/adapters/33across" "github.com/prebid/prebid-server/adapters/adform" @@ -35,6 +33,8 @@ import ( "github.com/prebid/prebid-server/adapters/gumgum" "github.com/prebid/prebid-server/adapters/improvedigital" "github.com/prebid/prebid-server/adapters/ix" + "github.com/prebid/prebid-server/adapters/kidoz" + "github.com/prebid/prebid-server/adapters/kubient" "github.com/prebid/prebid-server/adapters/lifestreet" "github.com/prebid/prebid-server/adapters/lockerdome" "github.com/prebid/prebid-server/adapters/marsmedia" @@ -101,6 +101,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint), openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint), + openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint), openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), diff --git a/go.mod b/go.mod index af4bf5570a5..ea1f65efaa4 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 + github.com/xorcare/pointer v1.1.0 github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect diff --git a/go.sum b/go.sum index 06f07b1ece0..6d215da0af5 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 h1:BcMExZAULPkihVZ7UJXK7t8rwGqisXFw75tILnafhBY= github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xorcare/pointer v1.1.0 h1:sFwXOhRF8QZ0tyVZrtxWGIoVZNEmRzBCaFWdONPQIUM= +github.com/xorcare/pointer v1.1.0/go.mod h1:6KLhkOh6YbuvZkT4YbxIbR/wzLBjyMxOiNzZhJTor2Y= github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d h1:yJIizrfO599ot2kQ6Af1enICnwBD3XoxgX3MrMwot2M= github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go index c9c6f36332b..768128c96d6 100644 --- a/openrtb_ext/bid.go +++ b/openrtb_ext/bid.go @@ -42,9 +42,9 @@ type BidType string const ( BidTypeBanner BidType = "banner" - BidTypeVideo = "video" - BidTypeAudio = "audio" - BidTypeNative = "native" + BidTypeVideo BidType = "video" + BidTypeAudio BidType = "audio" + BidTypeNative BidType = "native" ) func BidTypes() []BidType { diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 627842f57ff..00c25f8a3f0 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -47,6 +47,7 @@ const ( BidderGumGum BidderName = "gumgum" BidderImprovedigital BidderName = "improvedigital" BidderIx BidderName = "ix" + BidderKidoz BidderName = "kidoz" BidderKubient BidderName = "kubient" BidderLifestreet BidderName = "lifestreet" BidderLockerDome BidderName = "lockerdome" @@ -107,6 +108,7 @@ var BidderMap = map[string]BidderName{ "gumgum": BidderGumGum, "improvedigital": BidderImprovedigital, "ix": BidderIx, + "kidoz": BidderKidoz, "kubient": BidderKubient, "lifestreet": BidderLifestreet, "lockerdome": BidderLockerDome, diff --git a/openrtb_ext/imp_kidoz.go b/openrtb_ext/imp_kidoz.go new file mode 100644 index 00000000000..45f9866a425 --- /dev/null +++ b/openrtb_ext/imp_kidoz.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtImpKidoz struct { + AccessToken string `json:"access_token"` + PublisherID string `json:"publisher_id"` +} diff --git a/static/bidder-info/kidoz.yaml b/static/bidder-info/kidoz.yaml new file mode 100644 index 00000000000..e2a9eee3fc7 --- /dev/null +++ b/static/bidder-info/kidoz.yaml @@ -0,0 +1,11 @@ +maintainer: + email: prebid-support@kidoz.net +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/kidoz.json b/static/bidder-params/kidoz.json new file mode 100644 index 00000000000..79e2edc2fd2 --- /dev/null +++ b/static/bidder-params/kidoz.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Kidoz Adapter Params", + "description": "A schema which validates params accepted by the Kidoz adapter", + "type": "object", + "properties": { + "access_token": { + "$ref": "#/definitions/non-empty-string", + "description": "Kidoz access_token" + }, + "publisher_id": { + "$ref": "#/definitions/non-empty-string", + "description": "Kidoz publisher_id" + } + }, + "definitions": { + "non-empty-string": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "access_token", + "publisher_id" + ] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 7aef9fa8b5a..3de64ec1eb0 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -74,6 +74,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderTappx: true, openrtb_ext.BidderKubient: true, openrtb_ext.BidderPubnative: true, + openrtb_ext.BidderKidoz: true, } for bidder, config := range cfg.Adapters { diff --git a/validate.sh b/validate.sh index b5210550393..b81ade344d2 100755 --- a/validate.sh +++ b/validate.sh @@ -27,11 +27,11 @@ GOGLOB="${GOGLOB/ docs/}" GOGLOB="${GOGLOB/ vendor/}" # Check that there are no formatting issues -GOFMT_LINES=`gofmt -s -l $GOGLOB | wc -l | xargs` +GOFMT_LINES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | wc -l | xargs` if $AUTOFMT; then # if there are files with formatting issues, they will be automatically corrected using the gofmt -w command if [[ $GOFMT_LINES -ne 0 ]]; then - FMT_FILES=`gofmt -s -l $GOGLOB | xargs` + FMT_FILES=`gofmt -s -l $GOGLOB | tr '\\\\' '/' | xargs` for FILE in $FMT_FILES; do echo "Running: gofmt -s -w $FILE" `gofmt -s -w $FILE` From fb768950a1f625f267c1cdbce65f6668a62e38c8 Mon Sep 17 00:00:00 2001 From: ACannuniRP <57228257+ACannuniRP@users.noreply.github.com> Date: Wed, 18 Mar 2020 15:14:43 +0000 Subject: [PATCH 032/318] Update auction.md (#1224) Fix type --- docs/endpoints/openrtb2/auction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index d670b092174..02183960791 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -336,7 +336,7 @@ Bids can be temporarily cached on the server by sending the following data as `r } ``` -Both `bids` and `vastxml` are optional, but one of the two is required. Thils property will have no effect +Both `bids` and `vastxml` are optional, but one of the two is required. This property will have no effect unless `request.ext.prebid.targeting` is also set in the request. If `bids` is present, Prebid Server will make a _best effort_ to include these extra From c3c87971d4dd1b151dec329f59fa00713c9ed95a Mon Sep 17 00:00:00 2001 From: ACannuniRP <57228257+ACannuniRP@users.noreply.github.com> Date: Wed, 18 Mar 2020 15:15:16 +0000 Subject: [PATCH 033/318] Update auction.md (#1225) Fix typo. --- docs/endpoints/openrtb2/auction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index 02183960791..7795ef5afe0 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -174,7 +174,7 @@ will be truncated to only include the first 20 characters. #### Cookie syncs Each Bidder should receive their own ID in the `request.user.buyeruid` property. -Prebid Server has three ways to popualte this field. In order of priority: +Prebid Server has three ways to populate this field. In order of priority: 1. If the request payload contains `request.user.buyeruid`, then that value will be sent to all Bidders. In most cases, this is probably a bad idea. From dcc062a84d34f67b42fe34b64cca55e73ef926e4 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Wed, 18 Mar 2020 08:53:23 -0700 Subject: [PATCH 034/318] Added logging to cache for video endpoint (#1220) * WIP added logging to cache for video endpoint * Updating cache call to use TTL from config * Updates from initial feedback * Log now includes HTTP headers * Fixed caching to use a new cache entry rather than appending to the VAST * Added feature where is query is set, the test flag is set in the request * Updated recorded response and handleError * Updates from code review comments * Changed recorded output to be only the debug ext * Removed extra marhal calls * Changed cache to be an endpoint dependency * Added debugLog struct to hold all debug related info * Numerous smaller changes * Further code cleanup and added unit tests for debug changes * Added missing error checks * Added unit test for error case --- endpoints/openrtb2/amp_auction.go | 5 +- endpoints/openrtb2/amp_auction_test.go | 2 +- endpoints/openrtb2/auction.go | 7 +- endpoints/openrtb2/auction_test.go | 14 +- endpoints/openrtb2/video_auction.go | 83 +++++++-- endpoints/openrtb2/video_auction_test.go | 167 ++++++++++++++++++- exchange/auction.go | 14 +- exchange/auction_test.go | 3 +- exchange/cachetest/debuglog_disabled.json | 54 ++++++ exchange/cachetest/debuglog_enabled.json | 58 +++++++ exchange/exchange.go | 31 +++- exchange/exchange_test.go | 28 +++- exchange/exchangetest/debuglog_disabled.json | 161 ++++++++++++++++++ exchange/exchangetest/debuglog_enabled.json | 161 ++++++++++++++++++ exchange/targeting_test.go | 2 +- router/router.go | 2 +- 16 files changed, 749 insertions(+), 43 deletions(-) create mode 100644 exchange/cachetest/debuglog_disabled.json create mode 100644 exchange/cachetest/debuglog_enabled.json create mode 100644 exchange/exchangetest/debuglog_disabled.json create mode 100644 exchange/exchangetest/debuglog_enabled.json diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index d92f9d0ae61..8edc1e13787 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -71,7 +71,8 @@ func NewAmpEndpoint( disabledBidders, defRequest, defReqJSON, - bidderMap}).AmpAuction), nil + bidderMap, + nil}).AmpAuction), nil } @@ -165,7 +166,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h return } - response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories) + response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories, nil) ao.AuctionResponse = response if err != nil { diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index c62a6a710d5..39d1e13c50d 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -902,7 +902,7 @@ type mockAmpExchange struct { lastRequest *openrtb.BidRequest } -func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest response := &openrtb.BidResponse{ diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index a0ed19e5fa4..d9c31eca98c 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -27,6 +27,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/prebid" + "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/privacy/ccpa" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" @@ -55,7 +56,8 @@ func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidato disabledBidders, defRequest, defReqJSON, - bidderMap}).Auction), nil + bidderMap, + nil}).Auction), nil } type endpointDeps struct { @@ -71,6 +73,7 @@ type endpointDeps struct { defaultRequest bool defReqJSON []byte bidderMap map[string]openrtb_ext.BidderName + cache prebid_cache_client.Client } func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { @@ -137,7 +140,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http return } - response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories) + response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories, nil) ao.Request = req ao.Response = response if err != nil { diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 89f0fa255df..74a70c69415 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -602,7 +602,7 @@ func TestStoredRequests(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - edep := &endpointDeps{&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, false, []byte{}, openrtb_ext.BidderMap} + edep := &endpointDeps{&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, false, []byte{}, openrtb_ext.BidderMap, nil} for i, requestData := range testStoredRequests { newRequest, errList := edep.processStoredRequests(context.Background(), json.RawMessage(requestData)) @@ -638,6 +638,7 @@ func TestOversizedRequest(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -670,6 +671,7 @@ func TestRequestSizeEdgeCase(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -807,6 +809,7 @@ func TestDisabledBidder(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -840,6 +843,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } errs := deps.validateImpExt(imp, nil, 0) assert.JSONEq(t, `{"appnexus":{"placement_id":555}}`, string(imp.Ext)) @@ -878,6 +882,7 @@ func TestCurrencyTrunc(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } ui := uint64(1) @@ -919,6 +924,7 @@ func TestCCPAInvalidValueWarning(t *testing.T) { false, []byte{}, openrtb_ext.BidderMap, + nil, } ui := uint64(1) @@ -953,7 +959,7 @@ type nobidExchange struct { gotRequest *openrtb.BidRequest } -func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { e.gotRequest = bidRequest return &openrtb.BidResponse{ ID: bidRequest.ID, @@ -964,7 +970,7 @@ func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.Bid type brokenExchange struct{} -func (e *brokenExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (e *brokenExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { return nil, errors.New("Critical, unrecoverable error.") } @@ -1324,7 +1330,7 @@ type mockExchange struct { lastRequest *openrtb.BidRequest } -func (m *mockExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (m *mockExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest return &openrtb.BidResponse{ SeatBid: []openrtb.SeatBid{{ diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 2a8663959a6..630a3f5acd3 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -14,6 +14,7 @@ import ( "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" + "github.com/gofrs/uuid" "github.com/prebid/prebid-server/errortypes" "github.com/golang/glog" @@ -24,20 +25,21 @@ import ( "github.com/prebid/prebid-server/exchange" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" + "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/usersync" ) var defaultRequestTimeout int64 = 5000 -func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, videoFetcher stored_requests.Fetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName) (httprouter.Handle, error) { +func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, videoFetcher stored_requests.Fetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, cache prebid_cache_client.Client) (httprouter.Handle, error) { if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { return nil, errors.New("NewVideoEndpoint requires non-nil arguments.") } defRequest := defReqJSON != nil && len(defReqJSON) > 0 - return httprouter.Handle((&endpointDeps{ex, validator, requestsById, videoFetcher, categories, cfg, met, pbsAnalytics, disabledBidders, defRequest, defReqJSON, bidderMap}).VideoAuctionEndpoint), nil + return httprouter.Handle((&endpointDeps{ex, validator, requestsById, videoFetcher, categories, cfg, met, pbsAnalytics, disabledBidders, defRequest, defReqJSON, bidderMap, cache}).VideoAuctionEndpoint), nil } /* @@ -79,7 +81,38 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re CookieFlag: pbsmetrics.CookieFlagUnknown, RequestStatus: pbsmetrics.RequestStatusOK, } + + debugQuery := r.URL.Query().Get("debug") + cacheTTL := int64(3600) + if deps.cfg.CacheURL.DefaultTTLs.Video > 0 { + cacheTTL = int64(deps.cfg.CacheURL.DefaultTTLs.Video) + } + debugLog := exchange.DebugLog{ + EnableDebug: strings.EqualFold(debugQuery, "true"), + CacheType: prebid_cache_client.TypeXML, + TTL: cacheTTL, + } + defer func() { + if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil { + debugLog.Data = fmt.Sprintf("", debugLog.Data) + data, err := json.Marshal(debugLog.Data) + if err == nil { + toCache := []prebid_cache_client.Cacheable{ + { + Type: debugLog.CacheType, + Data: data, + TTLSeconds: debugLog.TTL, + Key: "log_" + debugLog.CacheKey, + }, + } + if deps.cache != nil { + ctx, cancel := context.WithDeadline(context.Background(), start.Add(time.Duration(100)*time.Millisecond)) + defer cancel() + deps.cache.PutJson(ctx, toCache) + } + } + } deps.metricsEngine.RecordRequest(labels) deps.metricsEngine.RecordRequestTime(labels, time.Since(start)) deps.analytics.LogVideoObject(&vo) @@ -91,38 +124,46 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } requestJson, err := ioutil.ReadAll(lr) if err != nil { - handleError(&labels, w, []error{err}, &vo) + handleError(&labels, w, []error{err}, &vo, &debugLog) return } resolvedRequest := requestJson + if debugLog.EnableDebug { + debugLog.Data = fmt.Sprintf("Request:\n%s", string(requestJson)) + if headerBytes, err := json.Marshal(r.Header); err == nil { + debugLog.Data = fmt.Sprintf("%s\n\nHeaders:\n%s", debugLog.Data, string(headerBytes)) + } else { + debugLog.Data = fmt.Sprintf("%s\n\nUnable to marshal headers data\n", debugLog.Data) + } + } //load additional data - stored simplified req storedRequestId, err := getVideoStoredRequestId(requestJson) if err != nil { if deps.cfg.VideoStoredRequestRequired { - handleError(&labels, w, []error{err}, &vo) + handleError(&labels, w, []error{err}, &vo, &debugLog) return } } else { storedRequest, errs := deps.loadStoredVideoRequest(context.Background(), storedRequestId) if len(errs) > 0 { - handleError(&labels, w, errs, &vo) + handleError(&labels, w, errs, &vo, &debugLog) return } //merge incoming req with stored video req resolvedRequest, err = jsonpatch.MergePatch(storedRequest, requestJson) if err != nil { - handleError(&labels, w, []error{err}, &vo) + handleError(&labels, w, []error{err}, &vo, &debugLog) return } } //unmarshal and validate combined result videoBidReq, errL, podErrors := deps.parseVideoRequest(resolvedRequest, r.Header) if len(errL) > 0 { - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } @@ -132,13 +173,17 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re if deps.defaultRequest { if err := json.Unmarshal(deps.defReqJSON, bidReq); err != nil { err = fmt.Errorf("Invalid JSON in Default Request Settings: %s", err) - handleError(&labels, w, []error{err}, &vo) + handleError(&labels, w, []error{err}, &vo, &debugLog) return } } //create full open rtb req from full video request mergeData(videoBidReq, bidReq) + // If debug query param is set, force the response to enable test flag + if debugLog.EnableDebug { + bidReq.Test = 1 + } initialPodNumber := len(videoBidReq.PodConfig.Pods) if len(podErrors) > 0 { @@ -156,7 +201,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } err := errors.New(fmt.Sprintf("all pods are incorrect: %s", strings.Join(resPodErr, "; "))) errL = append(errL, err) - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } @@ -168,7 +213,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re errL = deps.validateRequest(bidReq) if len(errL) > 0 { - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } @@ -196,16 +241,16 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { errL = append(errL, acctIdErr) - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } //execute auction logic - response, err := deps.ex.HoldAuction(ctx, bidReq, usersyncs, labels, &deps.categories) + response, err := deps.ex.HoldAuction(ctx, bidReq, usersyncs, labels, &deps.categories, &debugLog) vo.Request = bidReq vo.Response = response if err != nil { errL := []error{err} - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } @@ -213,7 +258,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re bidResp, err := buildVideoResponse(response, podErrors) if err != nil { errL := []error{err} - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } if bidReq.Test == 1 { @@ -226,7 +271,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re //resp, err := json.Marshal(response) if err != nil { errL := []error{err} - handleError(&labels, w, errL, &vo) + handleError(&labels, w, errL, &vo, &debugLog) return } @@ -242,7 +287,13 @@ func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []P return videoReq } -func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, vo *analytics.VideoObject) { +func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, vo *analytics.VideoObject, debugLog *exchange.DebugLog) { + if debugLog != nil && debugLog.EnableDebug { + if rawUUID, err := uuid.NewV4(); err == nil { + debugLog.CacheKey = rawUUID.String() + } + errL = append(errL, fmt.Errorf("[Debug cache ID: %s]", debugLog.CacheKey)) + } labels.RequestStatus = pbsmetrics.RequestStatusErr var errors string var status int = http.StatusInternalServerError diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index a5ad62c9fa8..0199b43f610 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -17,6 +17,7 @@ import ( "github.com/prebid/prebid-server/exchange" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" + "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" metrics "github.com/rcrowley/go-metrics" @@ -171,6 +172,112 @@ func TestCreateBidExtensionExactDurTrueNoPriceRange(t *testing.T) { assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity, openrtb_ext.PriceGranularityFromString("med"), "Price granularity is incorrect") } +func TestVideoEndpointDebugQueryTrue(t *testing.T) { + ex := &mockExchangeVideo{ + cache: &mockCacheClient{}, + } + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + reqBody := string(getRequestPayload(t, reqData)) + req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody)) + recorder := httptest.NewRecorder() + + deps := mockDeps(t, ex) + deps.VideoAuctionEndpoint(recorder, req, nil) + + if ex.lastRequest == nil { + t.Fatalf("The request never made it into the Exchange.") + } + if !ex.cache.called { + t.Fatalf("Cache was not called when it should have been") + } + + respBytes := recorder.Body.Bytes() + resp := &openrtb_ext.BidResponseVideo{} + if err := json.Unmarshal(respBytes, resp); err != nil { + t.Fatalf("Unable to umarshal response.") + } + + assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") + assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") + assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + + assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") + assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") + + assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response") +} + +func TestVideoEndpointDebugQueryFalse(t *testing.T) { + ex := &mockExchangeVideo{ + cache: &mockCacheClient{}, + } + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + reqBody := string(getRequestPayload(t, reqData)) + req := httptest.NewRequest("POST", "/openrtb2/video?debug=123", strings.NewReader(reqBody)) + recorder := httptest.NewRecorder() + + deps := mockDeps(t, ex) + deps.VideoAuctionEndpoint(recorder, req, nil) + + if ex.lastRequest == nil { + t.Fatalf("The request never made it into the Exchange.") + } + if ex.cache.called { + t.Fatalf("Cache was called when it shouldn't have been") + } + + respBytes := recorder.Body.Bytes() + resp := &openrtb_ext.BidResponseVideo{} + if err := json.Unmarshal(respBytes, resp); err != nil { + t.Fatalf("Unable to umarshal response.") + } + + assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") + assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") + assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + + assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") + assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[1].Targeting, 3, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[2].Targeting, 5, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") + assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") + + assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response") +} + +func TestVideoEndpointDebugError(t *testing.T) { + ex := &mockExchangeVideo{ + cache: &mockCacheClient{}, + } + reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + reqBody := string(getRequestPayload(t, reqData)) + req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody)) + recorder := httptest.NewRecorder() + + deps := mockDeps(t, ex) + deps.VideoAuctionEndpoint(recorder, req, nil) + + if !ex.cache.called { + t.Fatalf("Cache was not called when it should have been") + } + + assert.Equal(t, recorder.Code, 500, "Should catch error in request") +} + func TestVideoEndpointNoPods(t *testing.T) { ex := &mockExchangeVideo{} reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json") @@ -714,7 +821,7 @@ func TestHandleError(t *testing.T) { recorder := httptest.NewRecorder() err1 := errors.New("Error for testing handleError 1") err2 := errors.New("Error for testing handleError 2") - handleError(&labels, recorder, []error{err1, err2}, &vo) + handleError(&labels, recorder, []error{err1, err2}, &vo, nil) assert.Equal(t, pbsmetrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error") assert.Equal(t, 500, recorder.Code, "Error status should be written to writer") @@ -820,6 +927,41 @@ func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) { } +func TestHandleErrorDebugLog(t *testing.T) { + vo := analytics.VideoObject{ + Status: 200, + Errors: make([]error, 0), + } + + labels := pbsmetrics.Labels{ + Source: pbsmetrics.DemandUnknown, + RType: pbsmetrics.ReqTypeVideo, + PubID: pbsmetrics.PublisherUnknown, + Browser: "test browser", + CookieFlag: pbsmetrics.CookieFlagUnknown, + RequestStatus: pbsmetrics.RequestStatusOK, + } + + recorder := httptest.NewRecorder() + err1 := errors.New("Error for testing handleError 1") + err2 := errors.New("Error for testing handleError 2") + debugLog := exchange.DebugLog{ + EnableDebug: true, + CacheType: prebid_cache_client.TypeXML, + Data: "test debug data", + TTL: int64(3600), + } + handleError(&labels, recorder, []error{err1, err2}, &vo, &debugLog) + + assert.Equal(t, pbsmetrics.RequestStatusErr, labels.RequestStatus, "labels.RequestStatus should indicate an error") + assert.Equal(t, 500, recorder.Code, "Error status should be written to writer") + assert.Equal(t, 500, vo.Status, "Analytics object should have error status") + assert.Equal(t, 3, len(vo.Errors), "New errors including debug cache ID should be appended to Analytics object Errors") + assert.Equal(t, "Error for testing handleError 1", vo.Errors[0].Error(), "Error in Analytics object should have test error message for first error") + assert.Equal(t, "Error for testing handleError 2", vo.Errors[1].Error(), "Error in Analytics object should have test error message for second error") + assert.NotEmpty(t, debugLog.CacheKey, "DebugLog CacheKey value should have been set") +} + func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} @@ -836,6 +978,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *p false, []byte{}, openrtb_ext.BidderMap, + nil, } return edep, theMetrics, mockModule @@ -875,11 +1018,27 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { false, []byte{}, openrtb_ext.BidderMap, + ex.cache, } return edep } +type mockCacheClient struct { + called bool +} + +func (m *mockCacheClient) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) { + if !m.called { + m.called = true + } + return []string{}, []error{} +} + +func (m *mockCacheClient) GetExtCacheData() (string, string) { + return "", "" +} + type mockVideoStoredReqFetcher struct { } @@ -889,10 +1048,14 @@ func (cf mockVideoStoredReqFetcher) FetchRequests(ctx context.Context, requestID type mockExchangeVideo struct { lastRequest *openrtb.BidRequest + cache *mockCacheClient } -func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest + if debugLog != nil && debugLog.EnableDebug { + m.cache.called = true + } ext := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"20.00","hb_pb_cat_dur":"20.00_395_30s","hb_size":"1x1", "hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video"},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`) return &openrtb.BidResponse{ SeatBid: []openrtb.SeatBid{{ diff --git a/exchange/auction.go b/exchange/auction.go index 2b9a8cb58fc..9909b78dd87 100644 --- a/exchange/auction.go +++ b/exchange/auction.go @@ -60,7 +60,7 @@ func (a *auction) setRoundedPrices(priceGranularity openrtb_ext.PriceGranularity a.roundedPrices = roundedPrices } -func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string) []error { +func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string, debugLog *DebugLog) []error { var bids, vast, includeBidderKeys, includeWinners bool = targData.includeCacheBids, targData.includeCacheVast, targData.includeBidderKeys, targData.includeWinners if !((bids || vast) && (includeBidderKeys || includeWinners)) { return nil @@ -147,6 +147,18 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, } } + if debugLog != nil && debugLog.EnableDebug { + debugLog.CacheKey = hbCacheID + if jsonBytes, err := json.Marshal(debugLog.Data); err == nil { + toCache = append(toCache, prebid_cache_client.Cacheable{ + Type: debugLog.CacheType, + Data: jsonBytes, + TTLSeconds: debugLog.TTL, + Key: "log_" + debugLog.CacheKey, + }) + } + } + ids, err := cache.PutJson(ctx, toCache) if err != nil { errs = append(errs, err...) diff --git a/exchange/auction_test.go b/exchange/auction_test.go index ea19732d82b..d23ff03e00a 100644 --- a/exchange/auction_test.go +++ b/exchange/auction_test.go @@ -188,7 +188,7 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) { winningBidsByBidder: winningBidsByBidder, roundedPrices: roundedPrices, } - _ = testAuction.doCache(ctx, cache, targData, &specData.BidRequest, 60, &specData.DefaultTTLs, bidCategory) + _ = testAuction.doCache(ctx, cache, targData, &specData.BidRequest, 60, &specData.DefaultTTLs, bidCategory, &specData.DebugLog) if len(specData.ExpectedCacheables) > len(cache.items) { t.Errorf("%s: [CACHE_ERROR] Less elements were cached than expected \n", fileDisplayName) @@ -232,6 +232,7 @@ type cacheSpec struct { TargetDataIncludeBidderKeys bool `json:"targetDataIncludeBidderKeys"` TargetDataIncludeCacheBids bool `json:"targetDataIncludeCacheBids"` TargetDataIncludeCacheVast bool `json:"targetDataIncludeCacheVast"` + DebugLog DebugLog `json:"debugLog,omitempty"` } type pbsBid struct { diff --git a/exchange/cachetest/debuglog_disabled.json b/exchange/cachetest/debuglog_disabled.json new file mode 100644 index 00000000000..675488c04d1 --- /dev/null +++ b/exchange/cachetest/debuglog_disabled.json @@ -0,0 +1,54 @@ +{ + "debugLog": { + "EnableDebug": false, + "CacheType": "xml", + "TTL": 3600, + "Data": "test debug data" + }, + "bidRequest": { + "imp": [{ + "id": "oneImp", + "exp": 600 + }, { + "id": "twoImp" + }] + }, + "pbsBids": [{ + "bid":{ + "id": "bidOne", + "impid": "oneImp", + "price": 7.64 + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "bidTwo", + "impid": "twoImp", + "price": 5.64 + }, + "bidType": "video", + "bidder": "pubmatic" + }], + "expectedCacheables": [ + { + "Type": "json", + "TTLSeconds": 660, + "Data": "{\"id\": \"bidOne\", \"impid\": \"oneImp\", \"price\": 7.64}" + }, { + "Type": "json", + "TTLSeconds": 3660, + "Data": "{\"id\": \"bidTwo\", \"impid\": \"twoImp\", \"price\": 5.64}" + } + ], + "defaultTTLs": { + "banner": 300, + "video": 3600, + "audio": 1800, + "native": 300 + }, + "targetDataIncludeWinners":true, + "targetDataIncludeBidderKeys":true, + "targetDataIncludeCacheBids":true, + "targetDataIncludeCacheVast":false +} diff --git a/exchange/cachetest/debuglog_enabled.json b/exchange/cachetest/debuglog_enabled.json new file mode 100644 index 00000000000..d4486558a54 --- /dev/null +++ b/exchange/cachetest/debuglog_enabled.json @@ -0,0 +1,58 @@ +{ + "debugLog": { + "EnableDebug": true, + "CacheType": "xml", + "TTL": 3600, + "Data": "test debug data" + }, + "bidRequest": { + "imp": [{ + "id": "oneImp", + "exp": 600 + }, { + "id": "twoImp" + }] + }, + "pbsBids": [{ + "bid":{ + "id": "bidOne", + "impid": "oneImp", + "price": 7.64 + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "bidTwo", + "impid": "twoImp", + "price": 5.64 + }, + "bidType": "video", + "bidder": "pubmatic" + }], + "expectedCacheables": [ + { + "Type": "json", + "TTLSeconds": 660, + "Data": "{\"id\": \"bidOne\", \"impid\": \"oneImp\", \"price\": 7.64}" + }, { + "Type": "json", + "TTLSeconds": 3660, + "Data": "{\"id\": \"bidTwo\", \"impid\": \"twoImp\", \"price\": 5.64}" + }, { + "Type": "xml", + "TTLSeconds": 3600, + "Data": "test debug data" + } + ], + "defaultTTLs": { + "banner": 300, + "video": 3600, + "audio": 1800, + "native": 300 + }, + "targetDataIncludeWinners":true, + "targetDataIncludeBidderKeys":true, + "targetDataIncludeCacheBids":true, + "targetDataIncludeCacheVast":false +} diff --git a/exchange/exchange.go b/exchange/exchange.go index ef10180a745..995add3d496 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -27,10 +27,18 @@ import ( "github.com/prebid/prebid-server/prebid_cache_client" ) +type DebugLog struct { + EnableDebug bool + CacheType prebid_cache_client.PayloadType + Data string + TTL int64 + CacheKey string +} + // Exchange runs Auctions. Implementations must be threadsafe, and will be shared across many goroutines. type Exchange interface { // HoldAuction executes an OpenRTB v2.5 Auction. - HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) + HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) } // IdFetcher can find the user's ID for a specific Bidder. @@ -78,7 +86,7 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con return e } -func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher) (*openrtb.BidResponse, error) { +func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) { // Snapshot of resolved bid request for debug if test request resolvedRequest, err := buildResolvedRequest(bidRequest) if err != nil { @@ -142,6 +150,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, cleanRequests, aliases, bidAdjustmentFactors, blabels, conversions) var auc *auction = nil + var bidResponseExt *openrtb_ext.ExtBidResponse = nil if anyBidsReturned { var bidCategory map[string]string @@ -162,7 +171,15 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque if targData != nil { auc.setRoundedPrices(targData.priceGranularity) - cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &e.defaultTTLs, bidCategory) + if debugLog != nil && debugLog.EnableDebug { + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errs) + if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + debugLog.Data = fmt.Sprintf("", debugLog.Data, string(bidRespExtBytes)) + } else { + errs = append(errs, errors.New("Unable to marshal response ext for debugging")) + } + } + cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &e.defaultTTLs, bidCategory, debugLog) if len(cacheErrs) > 0 { errs = append(errs, cacheErrs...) } @@ -176,7 +193,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } // Build the response - return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, errs) + return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, bidResponseExt, errs) } type DealTierInfo struct { @@ -394,7 +411,7 @@ func errsToBidderErrors(errs []error) []openrtb_ext.ExtBidderError { } // This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester -func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb.BidRequest, resolvedRequest json.RawMessage, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, errList []error) (*openrtb.BidResponse, error) { +func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb.BidRequest, resolvedRequest json.RawMessage, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, errList []error) (*openrtb.BidResponse, error) { bidResponse := new(openrtb.BidResponse) bidResponse.ID = bidRequest.ID @@ -417,7 +434,9 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ bidResponse.SeatBid = seatBids - bidResponseExt := e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errList) + if bidResponseExt == nil { + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errList) + } buffer := &bytes.Buffer{} enc := json.NewEncoder(buffer) enc.SetEscapeHTML(false) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 0a64bce0826..7217e609189 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -127,7 +127,7 @@ func TestCharacterEscape(t *testing.T) { var errList []error /* 4) Build bid response */ - bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, errList) + bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, nil, errList) /* 5) Assert we have no errors and one '&' character as we are supposed to */ if err != nil { @@ -279,7 +279,7 @@ func TestGetBidCacheInfo(t *testing.T) { var errList []error /* 4) Build bid response */ - bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, errList) + bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, nil, errList) /* 5) Assert we have no errors and the bid response we expected*/ assert.NoError(t, err, "[TestGetBidCacheInfo] buildBidResponse() threw an error") @@ -450,7 +450,7 @@ func TestBidResponseCurrency(t *testing.T) { // Run tests for i := range testCases { - actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, errList) + actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, nil, errList) assert.NoError(t, err, fmt.Sprintf("[TEST_FAILED] e.buildBidResponse resturns error in test: %s Error message: %s \n", testCases[i].description, err)) assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext)) } @@ -489,7 +489,7 @@ func TestRaceIntegration(t *testing.T) { } theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) ex := NewExchange(server.Client(), &wellBehavedCache{}, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()) - _, err := ex.HoldAuction(context.Background(), newRaceCheckingRequest(t), &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher) + _, err := ex.HoldAuction(context.Background(), newRaceCheckingRequest(t), &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -666,7 +666,7 @@ func TestPanicRecoveryHighLevel(t *testing.T) { if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - _, err := e.HoldAuction(context.Background(), request, &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher) + _, err := e.HoldAuction(context.Background(), request, &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -732,7 +732,11 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - bid, err := ex.HoldAuction(context.Background(), &spec.IncomingRequest.OrtbRequest, mockIdFetcher(spec.IncomingRequest.Usersyncs), pbsmetrics.Labels{}, &categoriesFetcher) + debugLog := &DebugLog{} + if spec.DebugLog != nil { + *debugLog = *spec.DebugLog + } + bid, err := ex.HoldAuction(context.Background(), &spec.IncomingRequest.OrtbRequest, mockIdFetcher(spec.IncomingRequest.Usersyncs), pbsmetrics.Labels{}, &categoriesFetcher, debugLog) responseTimes := extractResponseTimes(t, filename, bid) for _, bidderName := range biddersInAuction { if _, ok := responseTimes[bidderName]; !ok { @@ -751,6 +755,17 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { } } } + if spec.DebugLog != nil { + if spec.DebugLog.EnableDebug { + if len(debugLog.Data) <= len(spec.DebugLog.Data) { + t.Errorf("%s: DebugLog was not modified when it should have been", filename) + } + } else { + if !strings.EqualFold(spec.DebugLog.Data, debugLog.Data) { + t.Errorf("%s: DebugLog was modified when it shouldn't have been", filename) + } + } + } } func findBiddersInAuction(t *testing.T, context string, req *openrtb.BidRequest) []string { @@ -1588,6 +1603,7 @@ type exchangeSpec struct { OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` Response exchangeResponse `json:"response,omitempty"` EnforceCCPA bool `json:"enforceCcpa"` + DebugLog *DebugLog `json:"debuglog,omitempty"` } type exchangeRequest struct { diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json new file mode 100644 index 00000000000..0c24c121935 --- /dev/null +++ b/exchange/exchangetest/debuglog_disabled.json @@ -0,0 +1,161 @@ +{ + "debugLog": { + "EnableDebug": false, + "CacheType": "xml", + "TTL": 3600, + "Data": "test debug data" + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "includebrandcategory": { + "primaryadserver": 1, + "publisher":"", + "withcategory": true + } + } + } + } + }, + "usersyncs": { + "appnexus": "123" + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": ["IAB1-1"] + }, + "bidType": "video", + "bidVideo": { + "duration": 30, + "PrimaryCategory": "" + } + }, + { + "ortbBid": { + "id": "apn-other-bid", + "impid": "imp-id-2", + "price": 0.6, + "w": 300, + "h": 500, + "crid": "creative-3", + "cat": ["IAB1-2"] + }, + "bidType": "video" + } + ] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": ["IAB1-1"], + "ext": { + "prebid": { + "type": "video", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.20", + "hb_pb_appnexus": "0.20", + "hb_pb_cat_dur": "0.20_VideoGames_0s", + "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s", + "hb_size": "200x250", + "hb_size_appnexus": "200x250" + } + } + } + }, + { + "cat": ["IAB1-2"], + "crid": "creative-3", + "ext": { + "prebid": { + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.50", + "hb_pb_appnexus": "0.50", + "hb_pb_cat_dur": "0.50_HomeDecor_0s", + "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s", + "hb_size": "300x500", + "hb_size_appnexus": "300x500" + }, + "type": "video" + } + }, + "h": 500, + "id": "apn-other-bid", + "impid": "imp-id-2", + "price": 0.6, + "w": 300 + } + ] + } + ] + } + } + } + \ No newline at end of file diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json new file mode 100644 index 00000000000..281bf3a1b4e --- /dev/null +++ b/exchange/exchangetest/debuglog_enabled.json @@ -0,0 +1,161 @@ +{ + "debugLog": { + "EnableDebug": true, + "CacheType": "xml", + "TTL": 3600, + "Data": "test debug data" + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "includebrandcategory": { + "primaryadserver": 1, + "publisher":"", + "withcategory": true + } + } + } + } + }, + "usersyncs": { + "appnexus": "123" + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": ["IAB1-1"] + }, + "bidType": "video", + "bidVideo": { + "duration": 30, + "PrimaryCategory": "" + } + }, + { + "ortbBid": { + "id": "apn-other-bid", + "impid": "imp-id-2", + "price": 0.6, + "w": 300, + "h": 500, + "crid": "creative-3", + "cat": ["IAB1-2"] + }, + "bidType": "video" + } + ] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": ["IAB1-1"], + "ext": { + "prebid": { + "type": "video", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.20", + "hb_pb_appnexus": "0.20", + "hb_pb_cat_dur": "0.20_VideoGames_0s", + "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s", + "hb_size": "200x250", + "hb_size_appnexus": "200x250" + } + } + } + }, + { + "cat": ["IAB1-2"], + "crid": "creative-3", + "ext": { + "prebid": { + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.50", + "hb_pb_appnexus": "0.50", + "hb_pb_cat_dur": "0.50_HomeDecor_0s", + "hb_pb_cat_dur_appnex": "0.50_HomeDecor_0s", + "hb_size": "300x500", + "hb_size_appnexus": "300x500" + }, + "type": "video" + } + }, + "h": 500, + "id": "apn-other-bid", + "impid": "imp-id-2", + "price": 0.6, + "w": 300 + } + ] + } + ] + } + } + } + \ No newline at end of file diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 92b338f97fb..f86309684c6 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -106,7 +106,7 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - bidResp, err := ex.HoldAuction(context.Background(), req, &mockFetcher{}, pbsmetrics.Labels{}, &categoriesFetcher) + bidResp, err := ex.HoldAuction(context.Background(), req, &mockFetcher{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) if err != nil { t.Fatalf("Unexpected errors running auction: %v", err) diff --git a/router/router.go b/router/router.go index 7e713ca637a..8ac463b85a0 100644 --- a/router/router.go +++ b/router/router.go @@ -251,7 +251,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r glog.Fatalf("Failed to create the amp endpoint handler. %v", err) } - videoEndpoint, err := openrtb2.NewVideoEndpoint(theExchange, paramsValidator, fetcher, videoFetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) + videoEndpoint, err := openrtb2.NewVideoEndpoint(theExchange, paramsValidator, fetcher, videoFetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap, cacheClient) if err != nil { glog.Fatalf("Failed to create the video endpoint handler. %v", err) } From c7ead0710a10624a767d28a14ee3f1e3b7278ec7 Mon Sep 17 00:00:00 2001 From: Aadesh Date: Wed, 25 Mar 2020 14:58:02 -0400 Subject: [PATCH 035/318] added VISX vendor ID for usersyncing (#1229) Co-authored-by: Aadesh Patel --- adapters/visx/usersync.go | 2 +- adapters/visx/usersync_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/visx/usersync.go b/adapters/visx/usersync.go index 0ceb58c505f..fe1f5a42a10 100644 --- a/adapters/visx/usersync.go +++ b/adapters/visx/usersync.go @@ -8,5 +8,5 @@ import ( ) func NewVisxSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("visx", 0, temp, adapters.SyncTypeRedirect) + return adapters.NewSyncer("visx", 154, temp, adapters.SyncTypeRedirect) } diff --git a/adapters/visx/usersync_test.go b/adapters/visx/usersync_test.go index 01e80e644c5..a77136c9240 100644 --- a/adapters/visx/usersync_test.go +++ b/adapters/visx/usersync_test.go @@ -30,6 +30,6 @@ func TestVisxSyncer(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "https://t.visx.net/s2s_sync?gdpr=A&gdpr_consent=B&us_privacy=C&redir=%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3DA%26gdpr_consent%3DB%26uid%3D%24%7BUUID%7D", syncInfo.URL) assert.Equal(t, "redirect", syncInfo.Type) - assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.EqualValues(t, 154, syncer.GDPRVendorID()) assert.Equal(t, false, syncInfo.SupportCORS) } From 145c5259042a09903a29f7b998d631f1782b92c7 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 25 Mar 2020 16:59:19 -0400 Subject: [PATCH 036/318] First pass at phase 1 TCF 2.0 support (#1228) * First pass at phase 1 TCF 2.0 support * minor fixes * Update go-gdpr library and fix stuff * Fixes for PR comments --- gdpr/gdpr.go | 14 +++++-- gdpr/impl.go | 28 ++++++++++---- gdpr/impl_test.go | 63 +++++++++++++++++++++++--------- gdpr/vendorlist-fetching.go | 46 ++++++++++++++--------- gdpr/vendorlist-fetching_test.go | 24 ++++++------ go.mod | 2 +- go.sum | 4 +- 7 files changed, 122 insertions(+), 59 deletions(-) diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index bdba008a77a..a6b64203a95 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -25,6 +26,11 @@ type Permissions interface { PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) } +const ( + tCF1 uint8 = 1 + tCF2 uint8 = 2 +) + // NewPermissions gets an instance of the Permissions for use elsewhere in the project. func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_ext.BidderName]uint16, client *http.Client) Permissions { // If the host doesn't buy into the IAB GDPR consent framework, then save some cycles and let all syncs happen. @@ -33,9 +39,11 @@ func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_ } return &permissionsImpl{ - cfg: cfg, - vendorIDs: vendorIDs, - fetchVendorList: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker), + cfg: cfg, + vendorIDs: vendorIDs, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tCF1), + tCF2: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tCF2)}, } } diff --git a/gdpr/impl.go b/gdpr/impl.go index 2fe6a67e99f..615c3a090c9 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -3,7 +3,9 @@ package gdpr import ( "context" - "github.com/prebid/go-gdpr/consentconstants" + "github.com/prebid/go-gdpr/api" + tcf1constants "github.com/prebid/go-gdpr/consentconstants" + consentconstants "github.com/prebid/go-gdpr/consentconstants/tcf2" "github.com/prebid/go-gdpr/vendorconsent" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/prebid-server/config" @@ -18,7 +20,7 @@ import ( type permissionsImpl struct { cfg config.GDPR vendorIDs map[openrtb_ext.BidderName]uint16 - fetchVendorList func(ctx context.Context, id uint16) (vendorlist.VendorList, error) + fetchVendorList map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error) } func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { @@ -71,10 +73,10 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen return false, nil } + // InfoStorageAccess is the same across TCF 1 and TCF 2 if vendor.Purpose(consentconstants.InfoStorageAccess) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && parsedConsent.VendorConsent(vendorID) { return true, nil } - return false, nil } @@ -93,14 +95,20 @@ func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent return false, nil } - if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(consentconstants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(consentconstants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) { - return true, nil + if parsedConsent.Version() == 2 { + // Need to add the location special purpose once the library supports it. + if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile)) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && parsedConsent.VendorConsent(vendorID) { + return true, nil + } + } else { + if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) { + return true, nil + } } - return false, nil } -func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, consent string) (parsedConsent vendorconsent.VendorConsents, vendor vendorlist.Vendor, err error) { +func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, consent string) (parsedConsent api.VendorConsents, vendor api.Vendor, err error) { parsedConsent, err = vendorconsent.ParseString(consent) if err != nil { err = &ErrorMalformedConsent{ @@ -110,7 +118,11 @@ func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, cons return } - vendorList, err := p.fetchVendorList(ctx, parsedConsent.VendorListVersion()) + version := parsedConsent.Version() + if version < 1 || version > 2 { + return + } + vendorList, err := p.fetchVendorList[version](ctx, parsedConsent.VendorListVersion()) if err != nil { return } diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index a1d4af3346d..8b89577d6c8 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -18,8 +18,11 @@ func TestNoConsentButAllowByDefault(t *testing.T) { HostVendorID: 3, UsersyncIfAmbiguous: true, }, - vendorIDs: nil, - fetchVendorList: failedListFetcher, + vendorIDs: nil, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: failedListFetcher, + tCF2: failedListFetcher, + }, } allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") assertBoolsEqual(t, true, allowSync) @@ -35,8 +38,11 @@ func TestNoConsentAndRejectByDefault(t *testing.T) { HostVendorID: 3, UsersyncIfAmbiguous: false, }, - vendorIDs: nil, - fetchVendorList: failedListFetcher, + vendorIDs: nil, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: failedListFetcher, + tCF2: failedListFetcher, + }, } allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") assertBoolsEqual(t, false, allowSync) @@ -63,9 +69,14 @@ func TestAllowedSyncs(t *testing.T) { openrtb_ext.BidderAppnexus: 2, openrtb_ext.BidderPubmatic: 3, }, - fetchVendorList: listFetcher(map[uint16]vendorlist.VendorList{ - 1: parseVendorListData(t, vendorListData), - }), + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + }, } allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABoAAAAAMw") @@ -94,9 +105,14 @@ func TestProhibitedPurposes(t *testing.T) { openrtb_ext.BidderAppnexus: 2, openrtb_ext.BidderPubmatic: 3, }, - fetchVendorList: listFetcher(map[uint16]vendorlist.VendorList{ - 1: parseVendorListData(t, vendorListData), - }), + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + }, } allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABAAAAAAMw") @@ -125,9 +141,14 @@ func TestProhibitedVendors(t *testing.T) { openrtb_ext.BidderAppnexus: 2, openrtb_ext.BidderPubmatic: 3, }, - fetchVendorList: listFetcher(map[uint16]vendorlist.VendorList{ - 1: parseVendorListData(t, vendorListData), - }), + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + }, } allowSync, err := perms.HostCookiesAllowed(context.Background(), "BOS2bx5OS2bx5ABABBAAABoAAAAAFA") @@ -144,7 +165,10 @@ func TestMalformedConsent(t *testing.T) { cfg: config.GDPR{ HostVendorID: 2, }, - fetchVendorList: listFetcher(nil), + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: listFetcher(nil), + tCF2: listFetcher(nil), + }, } sync, err := perms.HostCookiesAllowed(context.Background(), "BON") @@ -169,9 +193,14 @@ func TestAllowPersonalInfo(t *testing.T) { openrtb_ext.BidderAppnexus: 2, openrtb_ext.BidderPubmatic: 3, }, - fetchVendorList: listFetcher(map[uint16]vendorlist.VendorList{ - 1: parseVendorListData(t, vendorListData), - }), + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 1: parseVendorListData(t, vendorListData), + }), + }, } // PI needs both purposes to succeed diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index d492e9e5e11..987622a6a8a 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -11,12 +11,14 @@ import ( "time" "github.com/golang/glog" + "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/vendorlist" + "github.com/prebid/go-gdpr/vendorlist2" "github.com/prebid/prebid-server/config" "golang.org/x/net/context/ctxhttp" ) -type saveVendors func(uint16, vendorlist.VendorList) +type saveVendors func(uint16, api.VendorList) // This file provides the vendorlist-fetching function for Prebid Server. // @@ -24,22 +26,22 @@ type saveVendors func(uint16, vendorlist.VendorList) // // Nothing in this file is exported. Public APIs can be found in gdpr.go -func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16) string) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { +func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, TCFVer uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { // These save and load functions can be used to store & retrieve lists from our cache. save, load := newVendorListCache() withTimeout, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout()) defer cancel() - populateCache(withTimeout, client, urlMaker, save) + populateCache(withTimeout, client, urlMaker, save, TCFVer) - saveOneSometimes := newOccasionalSaver(cfg.Timeouts.ActiveTimeout()) + saveOneSometimes := newOccasionalSaver(cfg.Timeouts.ActiveTimeout(), TCFVer) return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { list := load(id) if list != nil { return list, nil } - saveOneSometimes(ctx, client, urlMaker(id), save) + saveOneSometimes(ctx, client, urlMaker(id, TCFVer), save) list = load(id) if list != nil { return list, nil @@ -49,17 +51,23 @@ func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http } // populateCache saves all the known versions of the vendor list for future use. -func populateCache(ctx context.Context, client *http.Client, urlMaker func(uint16) string, saver saveVendors) { - latestVersion := saveOne(ctx, client, urlMaker(0), saver) +func populateCache(ctx context.Context, client *http.Client, urlMaker func(uint16, uint8) string, saver saveVendors, TCFVer uint8) { + latestVersion := saveOne(ctx, client, urlMaker(0, TCFVer), saver, TCFVer) for i := uint16(1); i < latestVersion; i++ { - saveOne(ctx, client, urlMaker(i), saver) + saveOne(ctx, client, urlMaker(i, TCFVer), saver, TCFVer) } } // Make a URL which can be used to fetch a given version of the Global Vendor List. If the version is 0, // this will fetch the latest version. -func vendorListURLMaker(version uint16) string { +func vendorListURLMaker(version uint16, TCFVer uint8) string { + if TCFVer == 2 { + if version == 0 { + return "https://vendorlist.consensu.org/v2/vendor-list.json" + } + return "https://vendorlist.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(version)) + ".json" + } if version == 0 { return "https://vendorlist.consensu.org/vendorlist.json" } @@ -71,7 +79,7 @@ func vendorListURLMaker(version uint16) string { // The goal here is to update quickly when new versions of the VendorList are released, but not wreck // server performance if a bad CMP starts sending us malformed consent strings that advertize a version // that doesn't exist yet. -func newOccasionalSaver(timeout time.Duration) func(ctx context.Context, client *http.Client, url string, saver saveVendors) { +func newOccasionalSaver(timeout time.Duration, TCFVer uint8) func(ctx context.Context, client *http.Client, url string, saver saveVendors) { lastSaved := &atomic.Value{} lastSaved.Store(time.Time{}) @@ -80,13 +88,13 @@ func newOccasionalSaver(timeout time.Duration) func(ctx context.Context, client if now.Sub(lastSaved.Load().(time.Time)).Minutes() > 10 { withTimeout, cancel := context.WithTimeout(ctx, timeout) defer cancel() - saveOne(withTimeout, client, url, saver) + saveOne(withTimeout, client, url, saver, TCFVer) lastSaved.Store(now) } } } -func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors) uint16 { +func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors, cTFVer uint8) uint16 { req, err := http.NewRequest("GET", url, nil) if err != nil { glog.Errorf("Failed to build GET %s request. Cookie syncs may be affected: %v", url, err) @@ -109,8 +117,12 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen glog.Errorf("GET %s returned %d. Cookie syncs may be affected.", url, resp.StatusCode) return 0 } - - newList, err := vendorlist.ParseEagerly(respBody) + var newList api.VendorList + if cTFVer == 2 { + newList, err = vendorlist2.ParseEagerly(respBody) + } else { + newList, err = vendorlist.ParseEagerly(respBody) + } if err != nil { glog.Errorf("GET %s returned malformed JSON. Cookie syncs may be affected. Error was %v. Body was %s", url, err, string(respBody)) return 0 @@ -120,13 +132,13 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen return newList.Version() } -func newVendorListCache() (save func(id uint16, list vendorlist.VendorList), load func(id uint16) vendorlist.VendorList) { +func newVendorListCache() (save func(id uint16, list api.VendorList), load func(id uint16) api.VendorList) { cache := &sync.Map{} - save = func(id uint16, list vendorlist.VendorList) { + save = func(id uint16, list api.VendorList) { cache.Store(id, list) } - load = func(id uint16) vendorlist.VendorList { + load = func(id uint16) api.VendorList { list, ok := cache.Load(id) if ok { return list.(vendorlist.VendorList) diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index cdde3c46a68..8197fa263bc 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -29,7 +29,7 @@ func TestVendorFetch(t *testing.T) { }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) list, err := fetcher(context.Background(), 1) assertNilErr(t, err) vendor := list.Vendor(32) @@ -61,7 +61,7 @@ func TestLazyFetch(t *testing.T) { }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) list, err := fetcher(context.Background(), 2) assertNilErr(t, err) @@ -83,7 +83,7 @@ func TestInitialTimeout(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Time{}) defer cancel() - fetcher := newVendorListFetcher(ctx, testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(ctx, testConfig(), server.Client(), testURLMaker(server), 1) _, err := fetcher(context.Background(), 1) // This should do a lazy fetch, even though the initial call failed assertNilErr(t, err) } @@ -106,7 +106,7 @@ func TestFetchThrottling(t *testing.T) { }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) _, err := fetcher(context.Background(), 2) assertNilErr(t, err) _, err = fetcher(context.Background(), 3) @@ -117,7 +117,7 @@ func TestMalformedVendorlistFetch(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{1: "{}"}))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) _, err := fetcher(context.Background(), 1) assertErr(t, err, false) } @@ -126,15 +126,17 @@ func TestMissingVendorlistFetch(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{1: "{}"}))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) _, err := fetcher(context.Background(), 2) assertErr(t, err, false) } func TestVendorListMaker(t *testing.T) { - assertStringsEqual(t, "https://vendorlist.consensu.org/vendorlist.json", vendorListURLMaker(0)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v-2/vendorlist.json", vendorListURLMaker(2)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v-12/vendorlist.json", vendorListURLMaker(12)) + assertStringsEqual(t, "https://vendorlist.consensu.org/vendorlist.json", vendorListURLMaker(0, 1)) + assertStringsEqual(t, "https://vendorlist.consensu.org/v-2/vendorlist.json", vendorListURLMaker(2, 1)) + assertStringsEqual(t, "https://vendorlist.consensu.org/v-12/vendorlist.json", vendorListURLMaker(12, 1)) + assertStringsEqual(t, "https://vendorlist.consensu.org/v2/vendor-list.json", vendorListURLMaker(0, 2)) + assertStringsEqual(t, "https://vendorlist.consensu.org/v2/archives/vendor-list-v7.json", vendorListURLMaker(7, 2)) } // mockServer returns a handler which returns the given response for each global vendor list version. @@ -201,9 +203,9 @@ func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purpos return string(data) } -func testURLMaker(server *httptest.Server) func(uint16) string { +func testURLMaker(server *httptest.Server) func(uint16, uint8) string { url := server.URL - return func(version uint16) string { + return func(version uint16, TCFVer uint8) string { return url + "?version=" + strconv.Itoa(int(version)) } } diff --git a/go.mod b/go.mod index ea1f65efaa4..387b8b9815c 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/onsi/gomega v1.7.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.2.0 // indirect - github.com/prebid/go-gdpr v0.6.0 + github.com/prebid/go-gdpr v0.7.0 github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 diff --git a/go.sum b/go.sum index 6d215da0af5..ad9caf5004b 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prebid/go-gdpr v0.6.0 h1:/GKrygGkUbsgd96HIkjAu7/6CHtRedvcojRtfAd4Igc= -github.com/prebid/go-gdpr v0.6.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= +github.com/prebid/go-gdpr v0.7.0 h1:m4E/FjUhTBMciDsd3lQlbzFyXLzNK+JQkFmInJpFAwc= +github.com/prebid/go-gdpr v0.7.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= From 163f33187cebd4dc87087ffd7f67392d73c89dea Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Thu, 26 Mar 2020 09:30:39 -0700 Subject: [PATCH 037/318] Updated price granularity unmarshal to accept empty values and ranges (#1230) --- openrtb_ext/request.go | 7 ++++--- openrtb_ext/request_test.go | 13 ++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index ee1a0cd0f8b..9d1456c9618 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -148,10 +148,11 @@ func (pg *PriceGranularity) UnmarshalJSON(b []byte) error { } prevMax = gr.Max } - } else { - return errors.New("Price granularity error: empty granularity definition supplied") + *pg = PriceGranularity(pgraw) + return nil } - *pg = PriceGranularity(pgraw) + // Default to medium if no ranges are specified + *pg = priceGranularityMed return nil } diff --git a/openrtb_ext/request_test.go b/openrtb_ext/request_test.go index 860334af98f..3291c4f9fb2 100644 --- a/openrtb_ext/request_test.go +++ b/openrtb_ext/request_test.go @@ -175,11 +175,22 @@ var validGranularityTests []granularityTestData = []granularityTestData{ }, }, }, + { + json: []byte(`{}`), + target: priceGranularityMed, + }, + { + json: []byte(`{"precision": 2}`), + target: priceGranularityMed, + }, + { + json: []byte(`{"precision": 2, "ranges":[]}`), + target: priceGranularityMed, + }, } func TestGranularityUnmarshalBad(t *testing.T) { tests := [][]byte{ - []byte(`{}`), []byte(`[]`), []byte(`{"precision": -1, "ranges": [{"max":20, "increment":0.5}]}`), []byte(`{"ranges":[{"max":20, "increment": -1}]}`), From 469986402cfcd680687fa3fcc2b1c23885aef31c Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Thu, 26 Mar 2020 20:33:30 +0300 Subject: [PATCH 038/318] Update vendorID for TheMediaGrid s2s Bid Adapter (#1232) --- adapters/grid/usersync.go | 2 +- adapters/grid/usersync_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/grid/usersync.go b/adapters/grid/usersync.go index ddf7d5db66b..afdc5db763c 100644 --- a/adapters/grid/usersync.go +++ b/adapters/grid/usersync.go @@ -8,5 +8,5 @@ import ( ) func NewGridSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("grid", 0, temp, adapters.SyncTypeRedirect) + return adapters.NewSyncer("grid", 686, temp, adapters.SyncTypeRedirect) } diff --git a/adapters/grid/usersync_test.go b/adapters/grid/usersync_test.go index 9b97f605a41..99730b5deb4 100644 --- a/adapters/grid/usersync_test.go +++ b/adapters/grid/usersync_test.go @@ -25,6 +25,6 @@ func TestGridSyncer(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dgrid%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL) assert.Equal(t, "redirect", syncInfo.Type) - assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.EqualValues(t, 686, syncer.GDPRVendorID()) assert.Equal(t, false, syncInfo.SupportCORS) } From 7e706f499e66ee8731c23f1b231db3530897c3db Mon Sep 17 00:00:00 2001 From: Aadesh Date: Fri, 27 Mar 2020 10:17:48 -0400 Subject: [PATCH 039/318] treat 204 from FAN as a no bids response (#1233) Co-authored-by: Aadesh Patel --- .../supplemental/no-bid-204.json | 91 +++++++++++++++++++ adapters/audienceNetwork/facebook.go | 6 ++ 2 files changed, 97 insertions(+) create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json new file mode 100644 index 00000000000..042c86bd7fd --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json @@ -0,0 +1,91 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}", + "ver": "1.1" + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + } + ], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-req-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "w": -1, + "h": -1 + }, + "tagid": "123_456" + } + ], + "ext": { + "appnexus": { + "hb_source": 5 + }, + "prebid": {} + }, + "site": { + "domain": "prebid.org", + "page": "prebid.org", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index 3ece7bb99e4..db7657f59b7 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -333,6 +333,12 @@ func modifyImpCustom(json []byte, imp *openrtb.Imp) ([]byte, error) { } func (this *FacebookAdapter) MakeBids(request *openrtb.BidRequest, adapterRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + /* No bid response */ + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + /* Any other http status codes outside of 200 and 204 should be treated as errors */ if response.StatusCode != http.StatusOK { msg := response.Headers.Get("x-fb-an-errors") return nil, []error{&errortypes.BadInput{ From e05369b20aaf542b08bb4c1f9c0e61d289789a3d Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 30 Mar 2020 07:48:41 -0700 Subject: [PATCH 040/318] AMP CCPA Fix (#1187) --- analytics/config/testFiles/test-20200303 | 0 endpoints/openrtb2/amp_auction.go | 90 ++- endpoints/openrtb2/amp_auction_test.go | 864 ++++++++++++----------- endpoints/openrtb2/auction.go | 19 +- endpoints/openrtb2/video_auction.go | 6 +- errortypes/code.go | 34 + errortypes/code_test.go | 24 + errortypes/errortypes.go | 101 +-- errortypes/severity.go | 63 ++ errortypes/severity_test.go | 143 ++++ exchange/exchange.go | 14 +- openrtb_ext/bidders.go | 5 + openrtb_ext/bidders_test.go | 20 +- privacy/ccpa/policy.go | 34 +- privacy/ccpa/policy_test.go | 150 +++- privacy/gdpr/policy.go | 7 + privacy/gdpr/policy_test.go | 29 + privacy/policies.go | 25 + privacy/policies_test.go | 42 ++ 19 files changed, 1082 insertions(+), 588 deletions(-) delete mode 100644 analytics/config/testFiles/test-20200303 create mode 100644 errortypes/code.go create mode 100644 errortypes/code_test.go create mode 100644 errortypes/severity.go create mode 100644 errortypes/severity_test.go diff --git a/analytics/config/testFiles/test-20200303 b/analytics/config/testFiles/test-20200303 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 8edc1e13787..97ac8d0caae 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -23,8 +23,6 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" "github.com/prebid/prebid-server/usersync" @@ -36,6 +34,7 @@ type AmpResponse struct { Targeting map[string]string `json:"targeting"` Debug *openrtb_ext.ExtResponseDebug `json:"debug,omitempty"` Errors map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"errors,omitempty"` + Warnings map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError `json:"warnings,omitempty"` } // NewAmpEndpoint modifies the OpenRTB endpoint to handle AMP requests. This will basically modify the parsing @@ -121,13 +120,13 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h w.Header().Set("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin") req, errL := deps.parseAmpRequest(r) + ao.Errors = append(ao.Errors, errL...) - if fatalError(errL) { + if errortypes.ContainsFatalError(errL) { w.WriteHeader(http.StatusBadRequest) - for _, err := range errL { + for _, err := range errortypes.FatalOnly(errL) { w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error()))) } - ao.Errors = append(ao.Errors, errL...) labels.RequestStatus = pbsmetrics.RequestStatusBadInput return } @@ -151,18 +150,18 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h // Blacklist account now that we have resolved the value if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { errL = append(errL, acctIdErr) - erVal := errortypes.DecodeError(acctIdErr) - if erVal == errortypes.BlacklistedAppCode || erVal == errortypes.BlacklistedAcctCode { + errCode := errortypes.ReadCode(acctIdErr) + if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode { w.WriteHeader(http.StatusServiceUnavailable) labels.RequestStatus = pbsmetrics.RequestStatusBlacklisted - } else { //erVal == errortypes.AcctRequiredCode + } else { w.WriteHeader(http.StatusBadRequest) labels.RequestStatus = pbsmetrics.RequestStatusBadInput } - for _, err := range errL { + for _, err := range errortypes.FatalOnly(errL) { w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error()))) } - ao.Errors = append(ao.Errors, errL...) + ao.Errors = append(ao.Errors, acctIdErr) return } @@ -206,6 +205,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h } } } + // Extract any errors var extResponse openrtb_ext.ExtBidResponse eRErr := json.Unmarshal(response.Ext, &extResponse) @@ -213,10 +213,20 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr)) } + warnings := make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError) + for _, v := range errortypes.WarningOnly(errL) { + bidderErr := openrtb_ext.ExtBidderError{ + Code: errortypes.ReadCode(v), + Message: v.Error(), + } + warnings[openrtb_ext.BidderNameGeneral] = append(warnings[openrtb_ext.BidderNameGeneral], bidderErr) + } + // Now JSONify the targets for the AMP response. ampResponse := AmpResponse{ Targeting: targets, Errors: extResponse.Errors, + Warnings: warnings, } ao.AmpTargetingValues = targets @@ -252,8 +262,8 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h // If the errors list has at least one element, then no guarantees are made about the returned request. func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openrtb.BidRequest, errs []error) { // Load the stored request for the AMP ID. - req, errs = deps.loadRequestJSONForAmp(httpRequest) - if len(errs) > 0 { + req, e := deps.loadRequestJSONForAmp(httpRequest) + if errs = append(errs, e...); errortypes.ContainsFatalError(errs) { return } @@ -261,18 +271,15 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr deps.setFieldsImplicitly(httpRequest, req) // Need to ensure cache and targeting are turned on - errs = defaultRequestExt(req) - if len(errs) > 0 { + e = defaultRequestExt(req) + if errs = append(errs, e...); errortypes.ContainsFatalError(errs) { return } // At this point, we should have a valid request that definitely has Targeting and Cache turned on - errL := deps.validateRequest(req) - if len(errL) > 0 { - errs = append(errs, errL...) - } - + e = deps.validateRequest(req) + errs = append(errs, e...) return } @@ -287,9 +294,6 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req return } - debugParam := httpRequest.FormValue("debug") - debug := debugParam == "1" - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(storedRequestTimeoutMillis)*time.Millisecond) defer cancel() @@ -309,7 +313,8 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req return } - if debug { + debugParam := httpRequest.FormValue("debug") + if debugParam == "1" { req.Test = 1 } @@ -336,18 +341,15 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req *req.Imp[0].Secure = 1 } - err := deps.overrideWithParams(httpRequest, req) - if err != nil { - errs = []error{err} - } - + errs = deps.overrideWithParams(httpRequest, req) return } -func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *openrtb.BidRequest) error { +func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *openrtb.BidRequest) []error { if req.Site == nil { req.Site = &openrtb.Site{} } + // Override the stored request sizes with AMP ones, if they exist. if req.Imp[0].Banner != nil { width := parseFormInt(httpRequest, "w", 0) @@ -383,16 +385,17 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope req.Imp[0].TagID = slot } - privacyPolicies := privacy.Policies{ - GDPR: gdpr.Policy{ - Consent: httpRequest.URL.Query().Get("gdpr_consent"), - }, - CCPA: ccpa.Policy{ - Value: httpRequest.URL.Query().Get("us_privacy"), - }, - } - if err := privacyPolicies.Write(req); err != nil { - return err + consent := readConsent(httpRequest.URL) + if consent != "" { + if policies, ok := privacy.ReadPoliciesFromConsent(consent); ok { + if err := policies.Write(req); err != nil { + return []error{err} + } + } else { + return []error{&errortypes.InvalidPrivacyConsent{ + Message: fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent), + }} + } } if timeout, err := strconv.ParseInt(httpRequest.FormValue("timeout"), 10, 64); err == nil { @@ -533,3 +536,12 @@ func setAmpExt(site *openrtb.Site, value string) { site.Ext = json.RawMessage(`{"amp":` + value + `}`) } } + +func readConsent(url *url.URL) string { + if v := url.Query().Get("consent_string"); v != "" { + return v + } + + // Fallback to 'gdpr_consent' for compatability until it's no longer used by AMP. + return url.Query().Get("gdpr_consent") +} diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 39d1e13c50d..b25d5b0cc8f 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -81,9 +81,8 @@ func TestGoodAmpRequests(t *testing.T) { if response.Debug != nil { t.Errorf("Debug present but not requested") } - if _, ok := response.Errors[openrtb_ext.BidderOpenx]; !ok { - t.Errorf("OpenX error message is not present. (%v)", response.Errors) - } + + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors, "errors") } } @@ -122,357 +121,472 @@ func TestAMPPageInfo(t *testing.T) { assert.Equal(t, "test.somepage.co.uk", exchange.lastRequest.Site.Domain) } -func TestConsentThroughEndpoint(t *testing.T) { - // gdpr consent string that will come inside our http.Request query - const consentString = "BOa71ZYOa71ZYAbABBENA8-AAAAbN7_______9______9uz_Gv_r_f__33e8_39v_h_7_-___m_-3zV4-_lvR11yPA1OrfIrwFhiAw" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that DOESN'T come with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(false, false, "", DigiTurstID) - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) +func TestGDPRConsent(t *testing.T) { + consent := "BONV8oqONXwgmADACHENAO7pqzAAppY" + existingConsent := "BONV8oqONXwgmADACHENAO7pqzAAppY" + + digitrust := &openrtb_ext.ExtUserDigiTrust{ + ID: "anyDigitrustID", + KeyV: 1, + Pref: 0, + } + + testCases := []struct { + description string + consent string + userExt *openrtb_ext.ExtUser + nilUser bool + expectedUserExt openrtb_ext.ExtUser + }{ + { + description: "Nil User", + consent: consent, + nilUser: true, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: consent, + }, + }, + { + description: "Nil User Ext", + consent: consent, + userExt: nil, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: consent, + }, + }, + { + description: "Overrides Existing Consent", + consent: consent, + userExt: &openrtb_ext.ExtUser{ + Consent: existingConsent, + }, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: consent, + }, + }, + { + description: "Overrides Existing Consent - With Sibling Data", + consent: consent, + userExt: &openrtb_ext.ExtUser{ + Consent: existingConsent, + DigiTrust: digitrust, + }, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: consent, + DigiTrust: digitrust, + }, + }, + { + description: "Does Not Override Existing Consent If Empty", + consent: "", + userExt: &openrtb_ext.ExtUser{ + Consent: existingConsent, + }, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: existingConsent, + }, + }, } - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), - } + for _, test := range testCases { + // Build Request + bid, err := getTestBidRequest(test.nilUser, test.userExt, true, nil) + if err != nil { + t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) + } - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} + // Simulated Stored Request Backend + stored := map[string]json.RawMessage{"1": json.RawMessage(bid)} + + // Build Exchange Endpoint + mockExchange := &mockAmpExchange{} + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + endpoint, _ := NewAmpEndpoint( + mockExchange, + newParamsValidator(t), + &mockAmpStoredReqFetcher{stored}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + metrics, + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + []byte{}, + openrtb_ext.BidderMap, + ) + + // Invoke Endpoint + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s", test.consent), nil) + responseRecorder := httptest.NewRecorder() + endpoint(responseRecorder, request, nil) + + // Parse Resonse + var response AmpResponse + if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) + } - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{stored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", consentString), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, request, nil) + // Assert Result + result := mockExchange.lastRequest + if !assert.NotNil(t, result, test.description+":lastRequest") { + return + } + if !assert.NotNil(t, result.User, test.description+":lastRequest.User") { + return + } + if !assert.NotNil(t, result.User.Ext, test.description+":lastRequest.User.Ext") { + return + } + var ue openrtb_ext.ExtUser + err = json.Unmarshal(result.User.Ext, &ue) + if !assert.NoError(t, err, test.description+":deserialize") { + return + } + assert.Equal(t, test.expectedUserExt, ue, test.description) + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors, test.description+":errors") + assert.Empty(t, response.Warnings, test.description+":warnings") + + // Invoke Endpoint With Legacy Param + requestLegacy := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", test.consent), nil) + responseRecorderLegacy := httptest.NewRecorder() + endpoint(responseRecorderLegacy, requestLegacy, nil) + + // Parse Resonse + var responseLegacy AmpResponse + if err := json.Unmarshal(responseRecorderLegacy.Body.Bytes(), &responseLegacy); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) + } - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User, "Resulting bid request should have a valid User field after passing consent string through endpoint") { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext, "Resulting bid request should have a valid Ext field after passing consent string through endpoint") { - return + // Assert Result With Legacy Param + resultLegacy := mockExchange.lastRequest + if !assert.NotNil(t, resultLegacy, test.description+":legacy:lastRequest") { + return + } + if !assert.NotNil(t, resultLegacy.User, test.description+":legacy:lastRequest.User") { + return + } + if !assert.NotNil(t, resultLegacy.User.Ext, test.description+":legacy:lastRequest.User.Ext") { + return + } + var ueLegacy openrtb_ext.ExtUser + err = json.Unmarshal(resultLegacy.User.Ext, &ueLegacy) + if !assert.NoError(t, err, test.description+":legacy:deserialize") { + return + } + assert.Equal(t, test.expectedUserExt, ueLegacy, test.description+":legacy") + assert.Equal(t, expectedErrorsFromHoldAuction, responseLegacy.Errors, test.description+":legacy:errors") + assert.Empty(t, responseLegacy.Warnings, test.description+":legacy:warnings") } - - // Assert string `consent` is found in the User.Ext at all - assert.NotContainsf(t, fullMarshaledBidRequest, "consent:"+consentString, "Expected bid request to contain consent string %s \n", consentString) - - // Assert the last request has a valid User object with a consent string equal to that on the URL query - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err, "Error unmarshalling last processed request") - - // Assert consent string found in `http.Request` was passed correctly to the `User.Ext` object - assert.Contains(t, string(request.URL.RawQuery), consentString, "http.Request should come with a consent string in its query") - assert.Equal(t, consentString, ue.Consent, "Consent string unsuccessfully passed to bid request through AMP endpoint") - - // Assert other user properties found originally in our bid request such as `DigiTrust` were not overwritten - assert.Equal(t, DigiTurstID, ue.DigiTrust.ID, "Passing GDPR consent through endpoint should not override http.Request ExtUser fields other than consent") } -func TestConsentThroughEndpointNilUser(t *testing.T) { - // gdpr consent string that will come inside our http.Request query - const consentString = "BOa71ZYOa71ZYAbABBENA8-AAAAbN7_______9______9uz_Gv_r_f__33e8_39v_h_7_-___m_-3zV4-_lvR11yPA1OrfIrwFhiAw" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that DOESN'T come with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(true, false, "", DigiTurstID) - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) - } - - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), +func TestCCPAConsent(t *testing.T) { + consent := "1NYN" + existingConsent := "1NNN" + + var gdpr int8 = 1 + + testCases := []struct { + description string + consent string + regsExt *openrtb_ext.ExtRegs + nilRegs bool + expectedRegExt openrtb_ext.ExtRegs + }{ + { + description: "Nil Regs", + consent: consent, + nilRegs: true, + expectedRegExt: openrtb_ext.ExtRegs{ + USPrivacy: consent, + }, + }, + { + description: "Nil Regs Ext", + consent: consent, + regsExt: nil, + expectedRegExt: openrtb_ext.ExtRegs{ + USPrivacy: consent, + }, + }, + { + description: "Overrides Existing Consent", + consent: consent, + regsExt: &openrtb_ext.ExtRegs{ + USPrivacy: existingConsent, + }, + expectedRegExt: openrtb_ext.ExtRegs{ + USPrivacy: consent, + }, + }, + { + description: "Overrides Existing Consent - With Sibling Data", + consent: consent, + regsExt: &openrtb_ext.ExtRegs{ + USPrivacy: existingConsent, + GDPR: &gdpr, + }, + expectedRegExt: openrtb_ext.ExtRegs{ + USPrivacy: consent, + GDPR: &gdpr, + }, + }, + { + description: "Does Not Override Existing Consent If Empty", + consent: "", + regsExt: &openrtb_ext.ExtRegs{ + USPrivacy: existingConsent, + }, + expectedRegExt: openrtb_ext.ExtRegs{ + USPrivacy: existingConsent, + }, + }, } - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} + for _, test := range testCases { + // Build Request + bid, err := getTestBidRequest(true, nil, test.nilRegs, test.regsExt) + if err != nil { + t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) + } - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{stored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", consentString), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, request, nil) + // Simulated Stored Request Backend + stored := map[string]json.RawMessage{"1": json.RawMessage(bid)} + + // Build Exchange Endpoint + mockExchange := &mockAmpExchange{} + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + endpoint, _ := NewAmpEndpoint( + mockExchange, + newParamsValidator(t), + &mockAmpStoredReqFetcher{stored}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + metrics, + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + []byte{}, + openrtb_ext.BidderMap, + ) + + // Invoke Endpoint + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s", test.consent), nil) + responseRecorder := httptest.NewRecorder() + endpoint(responseRecorder, request, nil) + + // Parse Resonse + var response AmpResponse + if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) + } - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User, "Resulting bid request should have a valid User field after passing consent string through endpoint") { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext, "Resulting bid request should have a valid User.Ext field after passing consent string through endpoint") { - return + // Assert Result + result := mockExchange.lastRequest + if !assert.NotNil(t, result, test.description+":lastRequest") { + return + } + if !assert.NotNil(t, result.Regs, test.description+":lastRequest.Regs") { + return + } + if !assert.NotNil(t, result.Regs.Ext, test.description+":lastRequest.Regs.Ext") { + return + } + var re openrtb_ext.ExtRegs + err = json.Unmarshal(result.Regs.Ext, &re) + if !assert.NoError(t, err, test.description+":deserialize") { + return + } + assert.Equal(t, test.expectedRegExt, re, test.description) + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors) + assert.Empty(t, response.Warnings) } - - // Assert string `consent` is found in the User.Ext at all - assert.NotContains(t, fullMarshaledBidRequest, "consent:"+consentString, "This bid request should not contain a consent string. It will be passed the one in the http.Request endpoint") - - // Assert the last request has a valid User object with a consent string equal to that on the URL query - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err, "Error unmarshalling last processed request") - - // Assert consent string found in `http.Request` was passed correctly to the `User.Ext` object - assert.Contains(t, string(request.URL.RawQuery), consentString, "http.Request should come with a consent string in its query") - assert.Equal(t, consentString, ue.Consent, "Consent string unsuccessfully passed to bid request through AMP endpoint") } -func TestConsentThroughEndpointNilUserExt(t *testing.T) { - // gdpr consent string that will come inside our http.Request query - const consentString = "BOa71ZYOa71ZYAbABBENA8-AAAAbN7_______9______9uz_Gv_r_f__33e8_39v_h_7_-___m_-3zV4-_lvR11yPA1OrfIrwFhiAw" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that DOESN'T come with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(false, true, "some-consent-string", DigiTurstID) +func TestNoConsent(t *testing.T) { + // Build Request + bid, err := getTestBidRequest(true, nil, true, nil) if err != nil { t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) } - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), - } - - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} + // Simulated Stored Request Backend + stored := map[string]json.RawMessage{"1": json.RawMessage(bid)} + // Build Exchange Endpoint + mockExchange := &mockAmpExchange{} + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) endpoint, _ := NewAmpEndpoint( - exchange, + mockExchange, newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, + metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap, ) - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", consentString), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, request, nil) - - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User, "Resulting bid request should have a valid User field after passing consent string through endpoint") { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext, "Resulting bid request should have a valid Ext field after passing consent string through endpoint") { - return - } - // Assert string `consent` is found in the User.Ext at all - assert.NotContains(t, fullMarshaledBidRequest, "consent:"+consentString, "This bid request should not contain a consent string. It will be passed the one in the http.Request endpoint") + // Invoke Endpoint + request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil) + responseRecorder := httptest.NewRecorder() + endpoint(responseRecorder, request, nil) - // Assert the last request has a valid User object with a consent string equal to that on the URL query - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err, "Error unmarshalling last processed request") + // Parse Resonse + var response AmpResponse + if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) + } - // Assert consent string found in `http.Request` was passed correctly to the `User.Ext` object - assert.Contains(t, string(request.URL.RawQuery), consentString, "http.Request should come with a consent string in its query") - assert.Equal(t, consentString, ue.Consent, "Consent string unsuccessfully passed to bid request through AMP endpoint") + // Assert Result + result := mockExchange.lastRequest + assert.NotNil(t, result, "lastRequest") + assert.Nil(t, result.User, "lastRequest.User") + assert.Nil(t, result.Regs, "lastRequest.Regs") + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors) + assert.Empty(t, response.Warnings) } -func TestSubstituteRequestConsentWithEndpointConsent(t *testing.T) { - // gdpr consent string that will come inside our http.Request query - const consentString = "BOa71ZYOa71ZYAbABBENA8-AAAAbN7_______9______9uz_Gv_r_f__33e8_39v_h_7_-___m_-3zV4-_lvR11yPA1OrfIrwFhiAw" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that comes with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(false, false, "some-consent-string", "digitrustId") +func TestInvalidConsent(t *testing.T) { + // Build Request + bid, err := getTestBidRequest(true, nil, true, nil) if err != nil { t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) } - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), - } - - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} + // Simulated Stored Request Backend + stored := map[string]json.RawMessage{"1": json.RawMessage(bid)} + // Build Exchange Endpoint + mockExchange := &mockAmpExchange{} + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) endpoint, _ := NewAmpEndpoint( - exchange, + mockExchange, newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, + metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap, ) - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", consentString), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, request, nil) - - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User) { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext) { - return - } - // Assert the last request has a valid User object with a consent string equal to that on the URL query - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err) - // Assert consent string found in `http.Request` was passed correctly to the `User.Ext` object - assert.Contains(t, string(request.URL.RawQuery), consentString) - assert.Equal(t, consentString, ue.Consent) + // Invoke Endpoint + invalidConsent := "invalid" + request := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&consent_string="+invalidConsent, nil) + responseRecorder := httptest.NewRecorder() + endpoint(responseRecorder, request, nil) - // Assert other user properties found originally in our bid request such as `DigiTrust` were not overwritten - assert.Equal(t, DigiTurstID, ue.DigiTrust.ID) -} - -func TestDontSubstituteRequestConsentWithBlankEndpointConsent(t *testing.T) { - // Blank gdpr consent string that will come inside our http.Request query - const httpURLConsentString = "" - const PrebidConsentString = "some-consent-string" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that comes with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(false, false, PrebidConsentString, "digitrustId") - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) - } - - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), + // Parse Resonse + var response AmpResponse + if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) } - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} - - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{stored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&gdpr_consent=%s", httpURLConsentString), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, request, nil) - - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User) { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext) { - return + // Assert Result + expectedWarnings := map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError{ + openrtb_ext.BidderNameGeneral: { + { + Code: 10001, + Message: "Consent '" + invalidConsent + "' is not recognized as either CCPA or GDPR TCF.", + }, + }, } - // Assert the last request has a valid User object with a consent string equal to that on the PBS request - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err) - - // Assert consent string found in the PBS request was passed correctly to the `User.Ext` object - assert.Equal(t, PrebidConsentString, ue.Consent) + result := mockExchange.lastRequest + assert.NotNil(t, result, "lastRequest") + assert.Nil(t, result.User, "lastRequest.User") + assert.Nil(t, result.Regs, "lastRequest.Regs") + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors) + assert.Equal(t, expectedWarnings, response.Warnings) } -func TestDontSubstituteRequestConsentNoEndpointConsent(t *testing.T) { - // Blank gdpr consent string that will come inside our http.Request query - const PrebidConsentString = "some-consent-string" - const DigiTurstID = "digitrustId" - - // Generate a marshaled openrtb.BidRequest that comes with a gdpr consent string - fullMarshaledBidRequest, err := getTestBidRequest(false, false, PrebidConsentString, "digitrustId") - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) - } - - stored := map[string]json.RawMessage{ - "1": json.RawMessage(fullMarshaledBidRequest), +func TestNewAndLegacyConsentBothProvided(t *testing.T) { + validConsentGDPR1 := "BOu5On0Ou5On0ADACHENAO7pqzAAppY" + validConsentGDPR2 := "BONV8oqONXwgmADACHENAO7pqzAAppY" + + testCases := []struct { + description string + consent string + consentLegacy string + userExt *openrtb_ext.ExtUser + expectedUserExt openrtb_ext.ExtUser + }{ + { + description: "New Consent Wins", + consent: validConsentGDPR1, + consentLegacy: validConsentGDPR2, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: validConsentGDPR1, + }, + }, + { + description: "New Consent Wins - Reverse", + consent: validConsentGDPR2, + consentLegacy: validConsentGDPR1, + expectedUserExt: openrtb_ext.ExtUser{ + Consent: validConsentGDPR2, + }, + }, } - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - exchange := &mockAmpExchange{} + for _, test := range testCases { + // Build Request + bid, err := getTestBidRequest(false, nil, true, nil) + if err != nil { + t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) + } - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{stored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - consentStringLessHttpRequest := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1"), nil) - recorder := httptest.NewRecorder() - endpoint(recorder, consentStringLessHttpRequest, nil) + // Simulated Stored Request Backend + stored := map[string]json.RawMessage{"1": json.RawMessage(bid)} + + // Build Exchange Endpoint + mockExchange := &mockAmpExchange{} + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + endpoint, _ := NewAmpEndpoint( + mockExchange, + newParamsValidator(t), + &mockAmpStoredReqFetcher{stored}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + metrics, + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + []byte{}, + openrtb_ext.BidderMap, + ) + + // Invoke Endpoint + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&consent_string=%s&gdpr_consent=%s", test.consent, test.consentLegacy), nil) + responseRecorder := httptest.NewRecorder() + endpoint(responseRecorder, request, nil) + + // Parse Resonse + var response AmpResponse + if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + t.Fatalf("Error unmarshalling response: %s", err.Error()) + } - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", recorder.Code, recorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "User" field - if !assert.NotNil(t, exchange.lastRequest.User) { - return - } - // Assert our bidRequest had a valid "User.Ext" field - if !assert.NotNil(t, exchange.lastRequest.User.Ext) { - return + // Assert Result + result := mockExchange.lastRequest + if !assert.NotNil(t, result, test.description+":lastRequest") { + return + } + if !assert.NotNil(t, result.User, test.description+":lastRequest.User") { + return + } + if !assert.NotNil(t, result.User.Ext, test.description+":lastRequest.User.Ext") { + return + } + var ue openrtb_ext.ExtUser + err = json.Unmarshal(result.User.Ext, &ue) + if !assert.NoError(t, err, test.description+":deserialize") { + return + } + assert.Equal(t, test.expectedUserExt, ue, test.description) + assert.Equal(t, expectedErrorsFromHoldAuction, response.Errors) + assert.Empty(t, response.Warnings) } - // Assert the last request has a valid User object with a consent string equal to that on the PBS request - var ue openrtb_ext.ExtUser - err = json.Unmarshal(exchange.lastRequest.User.Ext, &ue) - assert.NoError(t, err) - - // Assert consent string found in the PBS request was passed correctly to the `User.Ext` object - assert.Equal(t, PrebidConsentString, ue.Consent) } func TestAMPSiteExt(t *testing.T) { @@ -739,102 +853,6 @@ func TestWidthOnly(t *testing.T) { }.execute(t) } -func TestCCPAPresent(t *testing.T) { - req, err := getTestBidRequest(false, false, "", "digitrustId") - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) - } - - reqStored := map[string]json.RawMessage{ - "1": json.RawMessage(req), - } - - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - - exchange := &mockAmpExchange{} - - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{reqStored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - - usPrivacy := "1YYN" - httpReq := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1&us_privacy="+usPrivacy, nil) - httpRecorder := httptest.NewRecorder() - endpoint(httpRecorder, httpReq, nil) - - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", httpRecorder.Code, httpRecorder.Body.String()) { - return - } - // Assert our bidRequest had a valid "Regs" field - if !assert.NotNil(t, exchange.lastRequest.Regs) { - return - } - // Assert our bidRequest had a valid "Regs.Ext" field - if !assert.NotNil(t, exchange.lastRequest.Regs.Ext) { - return - } - - var regs openrtb_ext.ExtRegs - err = json.Unmarshal(exchange.lastRequest.Regs.Ext, ®s) - assert.NoError(t, err) - assert.Equal(t, usPrivacy, regs.USPrivacy) -} - -func TestCCPANotPresent(t *testing.T) { - req, err := getTestBidRequest(false, false, "", "digitrustId") - if err != nil { - t.Fatalf("Failed to marshal the complete openrtb.BidRequest object %v", err) - } - - reqStored := map[string]json.RawMessage{ - "1": json.RawMessage(req), - } - - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - - exchange := &mockAmpExchange{} - - endpoint, _ := NewAmpEndpoint( - exchange, - newParamsValidator(t), - &mockAmpStoredReqFetcher{reqStored}, - empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize}, - theMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - []byte{}, - openrtb_ext.BidderMap, - ) - - httpReq := httptest.NewRequest("GET", "/openrtb2/auction/amp?tag_id=1", nil) - httpRecorder := httptest.NewRecorder() - endpoint(httpRecorder, httpReq, nil) - - // Assert our bidRequest was valid - if !assert.NotNil(t, exchange.lastRequest, "Endpoint responded with %d: %s", httpRecorder.Code, httpRecorder.Body.String()) { - return - } - - // Assert CCPA Signal Not Found - if exchange.lastRequest.Regs != nil && exchange.lastRequest.Regs.Ext != nil { - var regs openrtb_ext.ExtRegs - err = json.Unmarshal(exchange.lastRequest.Regs.Ext, ®s) - assert.NoError(t, err) - assert.Empty(t, regs.USPrivacy) - } -} - type formatOverrideSpec struct { width uint64 height uint64 @@ -902,6 +920,15 @@ type mockAmpExchange struct { lastRequest *openrtb.BidRequest } +var expectedErrorsFromHoldAuction map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError = map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError{ + openrtb_ext.BidderName("openx"): { + { + Code: 1, + Message: "The request exceeded the timeout allocated", + }, + }, +} + func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest @@ -926,39 +953,7 @@ func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.B return response, nil } -func getTestBidRequest(nilUser bool, nilExt bool, consentString string, digitrustID string) ([]byte, error) { - var userExt openrtb_ext.ExtUser - var userExtData []byte - var err error - - if consentString != "" { - userExt = openrtb_ext.ExtUser{ - Consent: consentString, - DigiTrust: &openrtb_ext.ExtUserDigiTrust{ - ID: digitrustID, - KeyV: 1, - Pref: 0, - }, - } - } else { - userExt = openrtb_ext.ExtUser{ - DigiTrust: &openrtb_ext.ExtUserDigiTrust{ - ID: digitrustID, - KeyV: 1, - Pref: 0, - }, - } - } - - if !nilExt { - userExtData, err = json.Marshal(userExt) - if err != nil { - return nil, err - } - } else { - userExtData = []byte("") - } - +func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, regsExt *openrtb_ext.ExtRegs) ([]byte, error) { var width uint64 = 300 var height uint64 = 300 bidRequest := &openrtb.BidRequest{ @@ -988,6 +983,16 @@ func getTestBidRequest(nilUser bool, nilExt bool, consentString string, digitrus Page: "some-page", }, } + + var userExtData []byte + if userExt != nil { + var err error + userExtData, err = json.Marshal(userExt) + if err != nil { + return nil, err + } + } + if !nilUser { bidRequest.User = &openrtb.User{ ID: "aUserId", @@ -995,5 +1000,22 @@ func getTestBidRequest(nilUser bool, nilExt bool, consentString string, digitrus Ext: userExtData, } } + + var regsExtData []byte + if regsExt != nil { + var err error + regsExtData, err = json.Marshal(regsExt) + if err != nil { + return nil, err + } + } + + if !nilRegs { + bidRequest.Regs = &openrtb.Regs{ + COPPA: 1, + Ext: regsExtData, + } + } + return json.Marshal(bidRequest) } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index d9c31eca98c..4594a4d5f64 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -106,7 +106,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http req, errL := deps.parseRequest(r) - if fatalError(errL) && writeError(errL, w, &labels) { + if errortypes.ContainsFatalError(errL) && writeError(errL, w, &labels) { return } @@ -326,7 +326,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { if len(errs) > 0 { errL = append(errL, errs...) } - if fatalError(errs) { + if errortypes.ContainsFatalError(errs) { return errL } } @@ -1177,8 +1177,8 @@ func writeError(errs []error, w http.ResponseWriter, labels *pbsmetrics.Labels) httpStatus := http.StatusBadRequest metricsStatus := pbsmetrics.RequestStatusBadInput for _, err := range errs { - erVal := errortypes.DecodeError(err) - if erVal == errortypes.BlacklistedAppCode || erVal == errortypes.BlacklistedAcctCode { + erVal := errortypes.ReadCode(err) + if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.BlacklistedAcctErrorCode { httpStatus = http.StatusServiceUnavailable metricsStatus = pbsmetrics.RequestStatusBlacklisted break @@ -1194,17 +1194,6 @@ func writeError(errs []error, w http.ResponseWriter, labels *pbsmetrics.Labels) return rc } -// Checks to see if an error in an error list is a fatal error -func fatalError(errL []error) bool { - for _, err := range errL { - errCode := errortypes.DecodeError(err) - if errCode != errortypes.BidderTemporarilyDisabledCode && errCode != errortypes.WarningCode { - return true - } - } - return false -} - // Returns the effective publisher ID func effectivePubID(pub *openrtb.Publisher) string { if pub != nil { diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 630a3f5acd3..0215eb4cff2 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -298,12 +298,12 @@ func handleError(labels *pbsmetrics.Labels, w http.ResponseWriter, errL []error, var errors string var status int = http.StatusInternalServerError for _, er := range errL { - erVal := errortypes.DecodeError(er) - if erVal == errortypes.BlacklistedAppCode || erVal == errortypes.BlacklistedAcctCode { + erVal := errortypes.ReadCode(er) + if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.BlacklistedAcctErrorCode { status = http.StatusServiceUnavailable labels.RequestStatus = pbsmetrics.RequestStatusBlacklisted break - } else if erVal == errortypes.AcctRequiredCode { + } else if erVal == errortypes.AcctRequiredErrorCode { status = http.StatusBadRequest labels.RequestStatus = pbsmetrics.RequestStatusBadInput break diff --git a/errortypes/code.go b/errortypes/code.go new file mode 100644 index 00000000000..80a5eb45542 --- /dev/null +++ b/errortypes/code.go @@ -0,0 +1,34 @@ +package errortypes + +// Defines numeric codes for well-known errors. +const ( + UnknownErrorCode = 999 + TimeoutErrorCode = iota + BadInputErrorCode + BlacklistedAppErrorCode + BadServerResponseErrorCode + FailedToRequestBidsErrorCode + BidderTemporarilyDisabledErrorCode + BlacklistedAcctErrorCode + AcctRequiredErrorCode +) + +// Defines numeric codes for well-known warnings. +const ( + UnknownWarningCode = 10999 + InvalidPrivacyConsentWarningCode = iota + 10000 +) + +// Coder provides an error or warning code with severity. +type Coder interface { + Code() int + Severity() Severity +} + +// ReadCode returns the error or warning code, or UnknownErrorCode if unavailable. +func ReadCode(err error) int { + if e, ok := err.(Coder); ok { + return e.Code() + } + return UnknownErrorCode +} diff --git a/errortypes/code_test.go b/errortypes/code_test.go new file mode 100644 index 00000000000..b2bf53b8340 --- /dev/null +++ b/errortypes/code_test.go @@ -0,0 +1,24 @@ +package errortypes + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadCodeWithCodeDefined(t *testing.T) { + err := &Timeout{Message: "code is defined"} + + result := ReadCode(err) + + assert.Equal(t, result, TimeoutErrorCode) +} + +func TestReadCodeWithCodeNotDefined(t *testing.T) { + err := errors.New("missing error code") + + result := ReadCode(err) + + assert.Equal(t, result, UnknownErrorCode) +} diff --git a/errortypes/errortypes.go b/errortypes/errortypes.go index 4bcea24f737..c953f9b7e08 100644 --- a/errortypes/errortypes.go +++ b/errortypes/errortypes.go @@ -1,28 +1,5 @@ package errortypes -// These define the error codes for all the errors enumerated in this package -// NoErrorCode is to reserve 0 for non error states. -const ( - NoErrorCode = iota - TimeoutCode - BadInputCode - BlacklistedAppCode - BadServerResponseCode - FailedToRequestBidsCode - BidderTemporarilyDisabledCode - BlacklistedAcctCode - AcctRequiredCode - WarningCode -) - -// We should use this code for any Error interface that is not in this package -const UnknownErrorCode = 999 - -// Coder provides an interface to use if we want to check the code of an error type created in this package. -type Coder interface { - Code() int -} - // Timeout should be used to flag that a bidder failed to return a response because the PBS timeout timer // expired before a result was received. // @@ -36,7 +13,11 @@ func (err *Timeout) Error() string { } func (err *Timeout) Code() int { - return TimeoutCode + return TimeoutErrorCode +} + +func (err *Timeout) Severity() Severity { + return SeverityFatal } // BadInput should be used when returning errors which are caused by bad input. @@ -52,7 +33,11 @@ func (err *BadInput) Error() string { } func (err *BadInput) Code() int { - return BadInputCode + return BadInputErrorCode +} + +func (err *BadInput) Severity() Severity { + return SeverityFatal } // BlacklistedApp should be used when a request App.ID matches an entry in the BlacklistedApps @@ -68,7 +53,11 @@ func (err *BlacklistedApp) Error() string { } func (err *BlacklistedApp) Code() int { - return BlacklistedAppCode + return BlacklistedAppErrorCode +} + +func (err *BlacklistedApp) Severity() Severity { + return SeverityFatal } // BlacklistedAcct should be used when a request account ID matches an entry in the BlacklistedAccts @@ -84,7 +73,11 @@ func (err *BlacklistedAcct) Error() string { } func (err *BlacklistedAcct) Code() int { - return BlacklistedAcctCode + return BlacklistedAcctErrorCode +} + +func (err *BlacklistedAcct) Severity() Severity { + return SeverityFatal } // AcctRequired should be used when the environment variable ACCOUNT_REQUIRED has been set to not @@ -100,7 +93,11 @@ func (err *AcctRequired) Error() string { } func (err *AcctRequired) Code() int { - return AcctRequiredCode + return AcctRequiredErrorCode +} + +func (err *AcctRequired) Severity() Severity { + return SeverityFatal } // BadServerResponse should be used when returning errors which are caused by bad/unexpected behavior on the remote server. @@ -121,7 +118,11 @@ func (err *BadServerResponse) Error() string { } func (err *BadServerResponse) Code() int { - return BadServerResponseCode + return BadServerResponseErrorCode +} + +func (err *BadServerResponse) Severity() Severity { + return SeverityFatal } // FailedToRequestBids is an error to cover the case where an adapter failed to generate any http requests to get bids, @@ -138,7 +139,11 @@ func (err *FailedToRequestBids) Error() string { } func (err *FailedToRequestBids) Code() int { - return FailedToRequestBidsCode + return FailedToRequestBidsErrorCode +} + +func (err *FailedToRequestBids) Severity() Severity { + return SeverityFatal } // BidderTemporarilyDisabled is used at the request validation step, where we want to continue processing as best we @@ -153,10 +158,14 @@ func (err *BidderTemporarilyDisabled) Error() string { } func (err *BidderTemporarilyDisabled) Code() int { - return BidderTemporarilyDisabledCode + return BidderTemporarilyDisabledErrorCode +} + +func (err *BidderTemporarilyDisabled) Severity() Severity { + return SeverityWarning } -// Warning is a generic warning type, not a serious error +// Warning is a generic non-fatal error. type Warning struct { Message string } @@ -165,15 +174,27 @@ func (err *Warning) Error() string { return err.Message } -// Code returns the error code func (err *Warning) Code() int { - return WarningCode + return UnknownWarningCode +} + +func (err *Warning) Severity() Severity { + return SeverityWarning +} + +// InvalidPrivacyConsent is a warning for when the privacy consent string is invalid and is ignored. +type InvalidPrivacyConsent struct { + Message string +} + +func (err *InvalidPrivacyConsent) Error() string { + return err.Message +} + +func (err *InvalidPrivacyConsent) Code() int { + return InvalidPrivacyConsentWarningCode } -// DecodeError provides the error code for an error, as defined above -func DecodeError(err error) int { - if ce, ok := err.(Coder); ok { - return ce.Code() - } - return UnknownErrorCode +func (err *InvalidPrivacyConsent) Severity() Severity { + return SeverityWarning } diff --git a/errortypes/severity.go b/errortypes/severity.go new file mode 100644 index 00000000000..0838b09592e --- /dev/null +++ b/errortypes/severity.go @@ -0,0 +1,63 @@ +package errortypes + +// Severity represents the severity level of a bid processing error. +type Severity int + +const ( + // SeverityUnknown represents an unknown severity level. + SeverityUnknown Severity = iota + + // SeverityFatal represents a fatal bid processing error which prevents a bid response. + SeverityFatal + + // SeverityWarning represents a non-fatal bid processing error where invalid or ambiguous + // data in the bid request was ignored. + SeverityWarning +) + +func isFatal(err error) bool { + s, ok := err.(Coder) + return !ok || s.Severity() == SeverityFatal +} + +func isWarning(err error) bool { + s, ok := err.(Coder) + return ok && s.Severity() == SeverityWarning +} + +// ContainsFatalError checks if the error list contains a fatal error. +func ContainsFatalError(errors []error) bool { + for _, err := range errors { + if isFatal(err) { + return true + } + } + + return false +} + +// FatalOnly returns a new error list with only the fatal severity errors. +func FatalOnly(errs []error) []error { + errsFatal := make([]error, 0, len(errs)) + + for _, err := range errs { + if isFatal(err) { + errsFatal = append(errsFatal, err) + } + } + + return errsFatal +} + +// WarningOnly returns a new error list with only the warning severity errors. +func WarningOnly(errs []error) []error { + errsWarning := make([]error, 0, len(errs)) + + for _, err := range errs { + if isWarning(err) { + errsWarning = append(errsWarning, err) + } + } + + return errsWarning +} diff --git a/errortypes/severity_test.go b/errortypes/severity_test.go new file mode 100644 index 00000000000..8330316a8d2 --- /dev/null +++ b/errortypes/severity_test.go @@ -0,0 +1,143 @@ +package errortypes + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +type stubError struct{ severity Severity } + +func (e *stubError) Error() string { return "anyMessage" } +func (e *stubError) Code() int { return 42 } +func (e *stubError) Severity() Severity { return e.severity } + +func TestContainsFatalError(t *testing.T) { + fatalError := &stubError{severity: SeverityFatal} + notFatalError := &stubError{severity: SeverityWarning} + unknownSeverityError := errors.New("anyError") + + testCases := []struct { + description string + errors []error + shouldBeFatal bool + }{ + { + description: "None", + errors: []error{}, + shouldBeFatal: false, + }, + { + description: "One - Fatal", + errors: []error{fatalError}, + shouldBeFatal: true, + }, + { + description: "One - Not Fatal", + errors: []error{notFatalError}, + shouldBeFatal: false, + }, + { + description: "One - Unknown Severity Same As Fatal", + errors: []error{unknownSeverityError}, + shouldBeFatal: true, + }, + { + description: "Mixed", + errors: []error{fatalError, notFatalError, unknownSeverityError}, + shouldBeFatal: true, + }, + } + + for _, tc := range testCases { + result := ContainsFatalError(tc.errors) + assert.Equal(t, tc.shouldBeFatal, result) + } +} + +func TestFatalOnly(t *testing.T) { + fatalError := &stubError{severity: SeverityFatal} + notFatalError := &stubError{severity: SeverityWarning} + unknownSeverityError := errors.New("anyError") + + testCases := []struct { + description string + errs []error + errsShouldBeFatal []error + }{ + { + description: "None", + errs: []error{}, + errsShouldBeFatal: []error{}, + }, + { + description: "One - Fatal", + errs: []error{fatalError}, + errsShouldBeFatal: []error{fatalError}, + }, + { + description: "One - Not Fatal", + errs: []error{notFatalError}, + errsShouldBeFatal: []error{}, + }, + { + description: "One - Unknown Severity Same As Fatal", + errs: []error{unknownSeverityError}, + errsShouldBeFatal: []error{unknownSeverityError}, + }, + { + description: "Mixed", + errs: []error{fatalError, notFatalError, unknownSeverityError}, + errsShouldBeFatal: []error{fatalError, unknownSeverityError}, + }, + } + + for _, tc := range testCases { + result := FatalOnly(tc.errs) + assert.ElementsMatch(t, tc.errsShouldBeFatal, result) + } +} + +func TestWarningOnly(t *testing.T) { + warningError := &stubError{severity: SeverityWarning} + notWarningError := &stubError{severity: SeverityFatal} + unknownSeverityError := errors.New("anyError") + + testCases := []struct { + description string + errs []error + errsShouldBeWarning []error + }{ + { + description: "None", + errs: []error{}, + errsShouldBeWarning: []error{}, + }, + { + description: "One - Warning", + errs: []error{warningError}, + errsShouldBeWarning: []error{warningError}, + }, + { + description: "One - Not Warning", + errs: []error{notWarningError}, + errsShouldBeWarning: []error{}, + }, + { + description: "One - Unknown Severity Not Warning", + errs: []error{unknownSeverityError}, + errsShouldBeWarning: []error{}, + }, + { + description: "One - Mixed", + errs: []error{warningError, notWarningError, unknownSeverityError}, + errsShouldBeWarning: []error{warningError}, + }, + } + + for _, tc := range testCases { + result := WarningOnly(tc.errs) + assert.ElementsMatch(t, tc.errsShouldBeWarning, result) + } +} diff --git a/exchange/exchange.go b/exchange/exchange.go index 995add3d496..3cab1880456 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -385,14 +385,14 @@ func errorsToMetric(errs []error) map[pbsmetrics.AdapterError]struct{} { ret := make(map[pbsmetrics.AdapterError]struct{}, len(errs)) var s struct{} for _, err := range errs { - switch errortypes.DecodeError(err) { - case errortypes.TimeoutCode: + switch errortypes.ReadCode(err) { + case errortypes.TimeoutErrorCode: ret[pbsmetrics.AdapterErrorTimeout] = s - case errortypes.BadInputCode: + case errortypes.BadInputErrorCode: ret[pbsmetrics.AdapterErrorBadInput] = s - case errortypes.BadServerResponseCode: + case errortypes.BadServerResponseErrorCode: ret[pbsmetrics.AdapterErrorBadServerResponse] = s - case errortypes.FailedToRequestBidsCode: + case errortypes.FailedToRequestBidsErrorCode: ret[pbsmetrics.AdapterErrorFailedToRequestBids] = s default: ret[pbsmetrics.AdapterErrorUnknown] = s @@ -404,7 +404,7 @@ func errorsToMetric(errs []error) map[pbsmetrics.AdapterError]struct{} { func errsToBidderErrors(errs []error) []openrtb_ext.ExtBidderError { serr := make([]openrtb_ext.ExtBidderError, len(errs)) for i := 0; i < len(errs); i++ { - serr[i].Code = errortypes.DecodeError(errs[i]) + serr[i].Code = errortypes.ReadCode(errs[i]) serr[i].Message = errs[i].Error() } return serr @@ -672,7 +672,7 @@ func (e *exchange) makeSeatBid(adapterBid *pbsOrtbSeatBid, adapter openrtb_ext.B ext, err := json.Marshal(sbExt) if err != nil { extError := openrtb_ext.ExtBidderError{ - Code: errortypes.DecodeError(err), + Code: errortypes.ReadCode(err), Message: fmt.Sprintf("Error writing SeatBid.Ext: %s", err.Error()), } adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, extError) diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 00c25f8a3f0..e3f186db333 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -17,8 +17,12 @@ const schemaDirectory = "static/bidder-params" // BidderName may refer to a bidder ID, or an Alias which is defined in the request. type BidderName string +// BidderNameGeneral is reserved for non-bidder specific messages when using a map keyed on the bidder name. +const BidderNameGeneral = BidderName("general") + // These names _must_ coincide with the bidder code in Prebid.js, if an adapter also exists in that project. // Please keep these (and the BidderMap) alphabetized to minimize merge conflicts among adapter submissions. +// The bidder name 'general' is not allowed since it has special meaning in message maps. const ( Bidder33Across BidderName = "33across" BidderAdform BidderName = "adform" @@ -80,6 +84,7 @@ const ( ) // BidderMap stores all the valid OpenRTB 2.x Bidders in the project. This map *must not* be mutated. +// The bidder name 'general' is not allowed since it has special meaning in message maps. var BidderMap = map[string]BidderName{ "33across": Bidder33Across, "adform": BidderAdform, diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go index 454a2454f31..d49b23237ed 100644 --- a/openrtb_ext/bidders_test.go +++ b/openrtb_ext/bidders_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" "github.com/xeipuuv/gojsonschema" ) @@ -49,21 +50,14 @@ func TestInvalidParams(t *testing.T) { } } -func TestBidderList(t *testing.T) { - list := BidderList() +func TestBidderListMatchesBidderMap(t *testing.T) { + bidders := BidderList() for _, bidderName := range BidderMap { - adapterInList(t, bidderName, list) + assert.Contains(t, bidders, bidderName) } } -func adapterInList(t *testing.T, a BidderName, l []BidderName) { - found := false - for _, n := range l { - if a == n { - found = true - } - } - if !found { - t.Errorf("Adapter %s not found in the adapter map!", a) - } +func TestBidderListDoesNotDefineGeneral(t *testing.T) { + bidders := BidderList() + assert.NotContains(t, bidders, BidderNameGeneral) } diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go index e34a35717a4..8b50e1112a9 100644 --- a/privacy/ccpa/policy.go +++ b/privacy/ccpa/policy.go @@ -3,6 +3,7 @@ package ccpa import ( "encoding/json" "errors" + "fmt" "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" @@ -49,35 +50,44 @@ func (p Policy) Write(req *openrtb.BidRequest) error { return err } -// Validate returns an error if the CCPA regulation value does not adhere to the IAB spec. +// Validate returns an error if the CCPA policy does not adhere to the IAB spec. func (p Policy) Validate() error { - if p.Value == "" { + if err := ValidateConsent(p.Value); err != nil { + return fmt.Errorf("request.regs.ext.us_privacy %s", err.Error()) + } + + return nil +} + +// ValidateConsent returns an error if the CCPA consent string does not adhere to the IAB spec. +func ValidateConsent(consent string) error { + if consent == "" { return nil } - if len(p.Value) != 4 { - return errors.New("request.regs.ext.us_privacy must contain 4 characters") + if len(consent) != 4 { + return errors.New("must contain 4 characters") } - if p.Value[0] != '1' { - return errors.New("request.regs.ext.us_privacy must specify version 1") + if consent[0] != '1' { + return errors.New("must specify version 1") } var c byte - c = p.Value[1] + c = consent[1] if c != 'N' && c != 'Y' && c != '-' { - return errors.New("request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice") + return errors.New("must specify 'N', 'Y', or '-' for the explicit notice") } - c = p.Value[2] + c = consent[2] if c != 'N' && c != 'Y' && c != '-' { - return errors.New("request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale") + return errors.New("must specify 'N', 'Y', or '-' for the opt-out sale") } - c = p.Value[3] + c = consent[3] if c != 'N' && c != 'Y' && c != '-' { - return errors.New("request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement") + return errors.New("must specify 'N', 'Y', or '-' for the limited service provider agreement") } return nil diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go index a70874ebbec..54613c89880 100644 --- a/privacy/ccpa/policy_test.go +++ b/privacy/ccpa/policy_test.go @@ -154,74 +154,148 @@ func TestWrite(t *testing.T) { func TestValidate(t *testing.T) { testCases := []struct { - description string - policy Policy - expected string + description string + policy Policy + expectedError string }{ { - description: "Valid", - policy: Policy{Value: "1NYN"}, - expected: "", + description: "Valid", + policy: Policy{Value: "1NYN"}, + expectedError: "", }, { - description: "Valid - Not Applicable", - policy: Policy{Value: "1---"}, - expected: "", + description: "Valid - Not Applicable", + policy: Policy{Value: "1---"}, + expectedError: "", }, { - description: "Valid - Empty", - policy: Policy{Value: ""}, - expected: "", + description: "Valid - Empty", + policy: Policy{Value: ""}, + expectedError: "", }, { - description: "Invalid Length", - policy: Policy{Value: "1NY"}, - expected: "request.regs.ext.us_privacy must contain 4 characters", + description: "Invalid Length", + policy: Policy{Value: "1NY"}, + expectedError: "request.regs.ext.us_privacy must contain 4 characters", }, { - description: "Invalid Version", - policy: Policy{Value: "2---"}, - expected: "request.regs.ext.us_privacy must specify version 1", + description: "Invalid Version", + policy: Policy{Value: "2---"}, + expectedError: "request.regs.ext.us_privacy must specify version 1", }, { - description: "Invalid Explicit Notice Char", - policy: Policy{Value: "1X--"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", + description: "Invalid Explicit Notice Char", + policy: Policy{Value: "1X--"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", }, { - description: "Invalid Explicit Notice Case", - policy: Policy{Value: "1y--"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", + description: "Invalid Explicit Notice Case", + policy: Policy{Value: "1y--"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", }, { - description: "Invalid Opt-Out Sale Char", - policy: Policy{Value: "1-X-"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Invalid Opt-Out Sale Char", + policy: Policy{Value: "1-X-"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", }, { - description: "Invalid Opt-Out Sale Case", - policy: Policy{Value: "1-y-"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Invalid Opt-Out Sale Case", + policy: Policy{Value: "1-y-"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", }, { - description: "Invalid LSPA Char", - policy: Policy{Value: "1--X"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Invalid LSPA Char", + policy: Policy{Value: "1--X"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", }, { - description: "Invalid LSPA Case", - policy: Policy{Value: "1--y"}, - expected: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Invalid LSPA Case", + policy: Policy{Value: "1--y"}, + expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", }, } for _, test := range testCases { result := test.policy.Validate() - if test.expected == "" { + if test.expectedError == "" { + assert.NoError(t, result, test.description) + } else { + assert.EqualError(t, result, test.expectedError, test.description) + } + } +} + +func TestValidateConsent(t *testing.T) { + testCases := []struct { + description string + consent string + expectedError string + }{ + { + description: "Valid", + consent: "1NYN", + expectedError: "", + }, + { + description: "Valid - Not Applicable", + consent: "1---", + expectedError: "", + }, + { + description: "Invalid Empty", + consent: "", + expectedError: "", + }, + { + description: "Invalid Length", + consent: "1NY", + expectedError: "must contain 4 characters", + }, + { + description: "Invalid Version", + consent: "2---", + expectedError: "must specify version 1", + }, + { + description: "Invalid Explicit Notice Char", + consent: "1X--", + expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + }, + { + description: "Invalid Explicit Notice Case", + consent: "1y--", + expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + }, + { + description: "Invalid Opt-Out Sale Char", + consent: "1-X-", + expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + }, + { + description: "Invalid Opt-Out Sale Case", + consent: "1-y-", + expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + }, + { + description: "Invalid LSPA Char", + consent: "1--X", + expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + }, + { + description: "Invalid LSPA Case", + consent: "1--y", + expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + }, + } + + for _, test := range testCases { + result := ValidateConsent(test.consent) + + if test.expectedError == "" { assert.NoError(t, result, test.description) } else { - assert.EqualError(t, result, test.expected, test.description) + assert.EqualError(t, result, test.expectedError, test.description) } } } diff --git a/privacy/gdpr/policy.go b/privacy/gdpr/policy.go index 61f95ac99c6..a4e1bc6aac7 100644 --- a/privacy/gdpr/policy.go +++ b/privacy/gdpr/policy.go @@ -5,6 +5,7 @@ import ( "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" + "github.com/prebid/go-gdpr/vendorconsent" ) // Policy represents the GDPR regulation for an OpenRTB bid request. @@ -32,3 +33,9 @@ func (p Policy) Write(req *openrtb.BidRequest) error { req.User.Ext, err = jsonparser.Set(req.User.Ext, []byte(`"`+p.Consent+`"`), "consent") return err } + +// ValidateConsent returns an error if the GDPR consent string does not adhere to the IAB TCF spec. +func ValidateConsent(consent string) error { + _, err := vendorconsent.ParseString(consent) + return err +} diff --git a/privacy/gdpr/policy_test.go b/privacy/gdpr/policy_test.go index 80bd882dada..00b97644971 100644 --- a/privacy/gdpr/policy_test.go +++ b/privacy/gdpr/policy_test.go @@ -72,3 +72,32 @@ func TestWrite(t *testing.T) { } } } + +func TestValidateConsent(t *testing.T) { + testCases := []struct { + description string + consent string + expectError bool + }{ + { + description: "Invalid", + consent: "", + expectError: true, + }, + { + description: "Valid", + consent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + expectError: false, + }, + } + + for _, test := range testCases { + result := ValidateConsent(test.consent) + + if test.expectError { + assert.Error(t, result, test.description) + } else { + assert.NoError(t, result, test.description) + } + } +} diff --git a/privacy/policies.go b/privacy/policies.go index ebe34ef5c3d..cb11c6d03a6 100644 --- a/privacy/policies.go +++ b/privacy/policies.go @@ -33,3 +33,28 @@ func writePolicies(req *openrtb.BidRequest, writers []policyWriter) error { return nil } + +// ReadPoliciesFromConsent inspects the consent string kind and sets the corresponding values in a new Policies object. +func ReadPoliciesFromConsent(consent string) (Policies, bool) { + if len(consent) == 0 { + return Policies{}, false + } + + if err := gdpr.ValidateConsent(consent); err == nil { + return Policies{ + GDPR: gdpr.Policy{ + Consent: consent, + }, + }, true + } + + if err := ccpa.ValidateConsent(consent); err == nil { + return Policies{ + CCPA: ccpa.Policy{ + Value: consent, + }, + }, true + } + + return Policies{}, false +} diff --git a/privacy/policies_test.go b/privacy/policies_test.go index 697942521fc..34fbe52d0e9 100644 --- a/privacy/policies_test.go +++ b/privacy/policies_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -75,3 +77,43 @@ func (m *mockPolicyWriter) Write(req *openrtb.BidRequest) error { args := m.Called(req) return args.Error(0) } + +func TestReadPoliciesFromConsent(t *testing.T) { + testCases := []struct { + description string + consent string + expectedResultValue Policies + expectedResultOK bool + }{ + { + description: "Empty String", + consent: "", + expectedResultValue: Policies{}, + expectedResultOK: false, + }, + { + description: "CCPA", + consent: "1NYN", + expectedResultValue: Policies{CCPA: ccpa.Policy{Value: "1NYN"}}, + expectedResultOK: true, + }, + { + description: "GDPR TCF 1.0", + consent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + expectedResultValue: Policies{GDPR: gdpr.Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY"}}, + expectedResultOK: true, + }, + { + description: "Invalid", + consent: "any invalid", + expectedResultValue: Policies{}, + expectedResultOK: false, + }, + } + + for _, test := range testCases { + resultValue, resultOK := ReadPoliciesFromConsent(test.consent) + assert.Equal(t, test.expectedResultValue, resultValue, test.description+":value") + assert.Equal(t, test.expectedResultOK, resultOK, test.description+":ok") + } +} From 7c009bac4f43b9c3b94671ccce58fe2f96024478 Mon Sep 17 00:00:00 2001 From: bretg Date: Mon, 30 Mar 2020 16:29:14 -0400 Subject: [PATCH 041/318] Update rubicon.md (#1234) --- docs/bidders/rubicon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bidders/rubicon.md b/docs/bidders/rubicon.md index 564acd9a3c4..ea376da427d 100644 --- a/docs/bidders/rubicon.md +++ b/docs/bidders/rubicon.md @@ -1,6 +1,6 @@ # Rubicon Bidder -Please contact your Rubicon Project account manager to get set up with a login and cookie-sync URL to run your own Prebid Server. You will be given instructions, including the available endpoints. +Please contact your Rubicon Project account manager or globalsupport@rubiconproject.com to get set up with a login and cookie-sync URL to run your own Prebid Server. You will be given instructions, including the available endpoints. **Note:** Rubicon is disabled by default. Please enable it in the app config if you wish to use it. Make sure you provide the correct cookie-sync URL in order for cookie-syncs to work properly. From 4b1f3e707ff9396517e87f2a488ba54b9c75c098 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 1 Apr 2020 15:35:52 -0400 Subject: [PATCH 042/318] adding schain interface (#1203) --- docs/endpoints/openrtb2/auction.md | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index 7795ef5afe0..0f03960190d 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -403,28 +403,21 @@ Example: PBS receiving a request for an interstitial imp and these parameters set, it will rewrite the format object within the interstitial imp. If the format array's first object is a size, PBS will take it as the max size for the interstitial. If that size is 1x1, it will look up the device's size and use that as the max size. If the format is not present, it will also use the device size as the max size. (1x1 support so that you don't have to omit the format object to use the device size) PBS with interstitial support will come preconfigured with a list of common ad sizes. Preferentially organized by weighing the larger and more common sizes first. But no guarantees to the ordering will be made. PBS will generate a new format list for the interstitial imp by traversing this list and picking the first 10 sizes that fall within the imp's max size and minimum percentage size. There will be no attempt to favor aspect ratios closer to the original size's aspect ratio. The limit of 10 is enforced to ensure we don't overload bidders with an overlong list. All the interstitial parameters will still be passed to the bidders, so they may recognize them and use their own size matching algorithms if they prefer. -#### Currency Support +#### Supply Chain Support -To set the desired 'ad server currency', use the standard OpenRTB `cur` attribute. Note that Prebid Server only looks at the first currency in the array. -``` -"cur": ["USD"] -``` +Basic supply chains are passed to Prebid Server on `source.ext.schain` and passed through to bid adapters. Prebid Server does not currently offer the ability to add a node to the supply chain. -If you want or need to define currency conversion rates (e.g. for currencies that your Prebid Server doesn't support), define ext.prebid.currency.rates. (Currently supported in PBS-Java only) +Bidder-specific schains (PBS-Java only): ``` -"ext": { - "prebid": { - "currency": { - "rates": { - "USD": { "UAH": 24.47, "ETB": 32.04 } - } - } - } -} +ext.prebid.schains: [ + { bidders: ["bidderA"], schain: { SCHAIN OBJECT 1}}, + { bidders: ["*"], schain: { SCHAIN OBJECT 2}} +] ``` +In this scenario, Prebid Server sends the first schain object to `bidderA` and the second schain object to everyone else. -If it exists, a rate defined in ext.prebid.currency.rates has the highest priority. If a currency rate doesn't exist in the request, the external file will be used. +If there's already an source.ext.schain and a bidder is named in ext.prebid.schains (or covered by the wildcard condition), ext.prebid.schains takes precedent. #### Stored Responses (PBS-Java only) From 40f433bfc30b47ae32fc302de1b64ef0cbcab086 Mon Sep 17 00:00:00 2001 From: bretg Date: Wed, 1 Apr 2020 17:13:21 -0400 Subject: [PATCH 043/318] added Rewarded Video section (#1200) also edited all examples so they include the full openRTB context --- docs/endpoints/openrtb2/auction.md | 179 ++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 54 deletions(-) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index 0f03960190d..bd421850d1f 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -95,8 +95,14 @@ If you find that some bidders use Gross bids, publishers can adjust for it with ``` { - "appnexus: 0.8, - "rubicon": 0.7 + "ext": { + "prebid": { + "bidadjustmentfactors": { + "appnexus: 0.8, + "rubicon": 0.7 + } + } + } } ``` @@ -114,17 +120,21 @@ to set these params on the response at `response.seatbid[i].bid[j].ext.prebid.ta ``` { - "pricegranularity": { - "precision": 2, - "ranges": [ - { + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [{ "max":20.00, "increment":0.10 // This is equivalent to the deprecated "pricegranularity": "medium" - } - ] - }, - "includewinners": false // Optional param defaulting to true - "includebidderkeys": false // Optional param defaulting to true + }] + }, + "includewinners": false, // Optional param defaulting to true + "includebidderkeys": false // Optional param defaulting to true + } + } + } } ``` The list of price granularity ranges must be given in order of increasing `max` values. If `precision` is omitted, it will default to `2`. The minimum of a range will be 0 or the previous `max`. Any cmp above the largest `max` will go in the `max` pricebucket. @@ -159,9 +169,20 @@ MediaType PriceGranularity (PBS-Java only) - when a single OpenRTB request conta ``` { - "hb_bidder_{bidderName}": "The seatbid.seat which contains this bid", - "hb_size_{bidderName}": "A string like '300x250' using bid.w and bid.h for this bid", - "hb_pb_{bidderName}": "The bid.cpm, rounded down based on the price granularity." + "seatbid": [{ + "bid": [{ + ... + "ext": { + "prebid": { + "targeting": { + "hb_bidder_{bidderName}": "The seatbid.seat which contains this bid", + "hb_size_{bidderName}": "A string like '300x250' using bid.w and bid.h for this bid", + "hb_pb_{bidderName}": "The bid.cpm, rounded down based on the price granularity." + } + } + } + }] + }] } ``` @@ -183,8 +204,16 @@ In most cases, this is probably a bad idea. ``` { - "appnexus": "some-appnexus-id", - "rubicon": "some-rubicon-id" + "user": { + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "some-appnexus-id", + "rubicon": "some-rubicon-id" + } + } + } + } } ``` @@ -245,11 +274,9 @@ This prevents breaking API changes as new Bidders are added to the project. For example, if the Request defines an alias like this: ``` -{ "aliases": { "appnexus": "rubicon" } -} ``` then any `imp.ext.appnexus` params will actually go to the **rubicon** adapter. @@ -273,19 +300,17 @@ For example, a request may return this in `response.ext` ``` { - "errors": { - "appnexus": [ - { - "code": 2, - "message": "A hybrid Banner/Audio Imp was offered, but Appnexus doesn't support Audio." - } - ], - "rubicon": [ - { - "code": 1, - "message": "The request exceeded the timeout allocated" - } - ] + "ext": { + "errors": { + "appnexus": [{ + "code": 2, + "message": "A hybrid Banner/Audio Imp was offered, but Appnexus doesn't support Audio." + }], + "rubicon": [{ + "code": 1, + "message": "The request exceeded the timeout allocated" + }] + } } } ``` @@ -319,7 +344,15 @@ A typical `storedrequest` value looks like this: ``` { - "id": "some-id" + "imp": [{ + "ext": { + "prebid": { + "storedrequest": { + "id": "some-id" + } + } + } + }] } ``` @@ -331,12 +364,18 @@ Bids can be temporarily cached on the server by sending the following data as `r ``` { - "bids": {}, - "vastxml": {} + "ext": { + "prebid": { + "cache": { + "bids": {}, + "vastxml": {} + } + } + } } ``` -Both `bids` and `vastxml` are optional, but one of the two is required. This property will have no effect +Both `bids` and `vastxml` are optional, but one of the two is required if you want to cache bids. This property will have no effect unless `request.ext.prebid.targeting` is also set in the request. If `bids` is present, Prebid Server will make a _best effort_ to include these extra @@ -403,8 +442,35 @@ Example: PBS receiving a request for an interstitial imp and these parameters set, it will rewrite the format object within the interstitial imp. If the format array's first object is a size, PBS will take it as the max size for the interstitial. If that size is 1x1, it will look up the device's size and use that as the max size. If the format is not present, it will also use the device size as the max size. (1x1 support so that you don't have to omit the format object to use the device size) PBS with interstitial support will come preconfigured with a list of common ad sizes. Preferentially organized by weighing the larger and more common sizes first. But no guarantees to the ordering will be made. PBS will generate a new format list for the interstitial imp by traversing this list and picking the first 10 sizes that fall within the imp's max size and minimum percentage size. There will be no attempt to favor aspect ratios closer to the original size's aspect ratio. The limit of 10 is enforced to ensure we don't overload bidders with an overlong list. All the interstitial parameters will still be passed to the bidders, so they may recognize them and use their own size matching algorithms if they prefer. +#### Currency Support + +To set the desired 'ad server currency', use the standard OpenRTB `cur` attribute. Note that Prebid Server only looks at the first currency in the array. + +``` + "cur": ["USD"] +``` + +If you want or need to define currency conversion rates (e.g. for currencies that your Prebid Server doesn't support), +define ext.prebid.currency.rates. (Currently supported in PBS-Java only) + +``` +"ext": { + "prebid": { + "currency": { + "rates": { + "USD": { "UAH": 24.47, "ETB": 32.04 } + } + } + } +} +``` + +If it exists, a rate defined in ext.prebid.currency.rates has the highest priority. +If a currency rate doesn't exist in the request, the external file will be used. + #### Supply Chain Support + Basic supply chains are passed to Prebid Server on `source.ext.schain` and passed through to bid adapters. Prebid Server does not currently offer the ability to add a node to the supply chain. Bidder-specific schains (PBS-Java only): @@ -419,6 +485,11 @@ In this scenario, Prebid Server sends the first schain object to `bidderA` and t If there's already an source.ext.schain and a bidder is named in ext.prebid.schains (or covered by the wildcard condition), ext.prebid.schains takes precedent. +#### Rewarded Video (PBS-Java only) + +Rewarded video is a way to incentivize users to watch ads by giving them 'points' for viewing an ad. A Prebid Server +client can declare a given adunit as eligible for rewards by declaring `imp.ext.prebid.is_rewarded_inventory:1`. + #### Stored Responses (PBS-Java only) While testing SDK and video integrations, it's important, but often difficult, to get consistent responses back from bidders that cover a range of scenarios like different CPM values, deals, etc. Prebid Server supports a debugging workflow in two ways: @@ -583,33 +654,33 @@ It specifies where in the OpenRTB request non-standard attributes should be pass ``` { - ext: { - prebid: { - data: { bidders: [ 'rubicon', 'appnexus' ] } // these are the bidders allowed to see protected data + "ext": { + "prebid": { + "data": { "bidders": [ "rubicon", "appnexus" ] } // these are the bidders allowed to see protected data } }, - site: { - keywords: "", - search: "", - ext: { + "site": { + "keywords": "", + "search": "", + "ext": { data: { GLOBAL CONTEXT DATA } // only seen by bidders named in ext.prebid.data.bidders[] } }, - user: { - keywords: "", - gender: "", - yob: 1999, - geo: {}, - ext: { + "user": { + "keywords": "", + "gender": "", + "yob": 1999, + "geo": {}, + "ext": { data: { GLOBAL USER DATA } // only seen by bidders named in ext.prebid.data.bidders[] } }, - imp: [ - ext: { - context: { - keywords: "", - search: "", - data: { ADUNIT SPECFIC CONTEXT DATA } // can be seen by all bidders + "imp": [ + "ext": { + "context": { + "keywords": "", + "search": "", + "data": { ADUNIT SPECFIC CONTEXT DATA } // can be seen by all bidders } } ] From 3665275ff778b6a95fe583053dedf4c0a1365529 Mon Sep 17 00:00:00 2001 From: Rade Popovic <32302052+nanointeractive@users.noreply.github.com> Date: Thu, 2 Apr 2020 23:40:42 +0200 Subject: [PATCH 044/318] nanointeractive adapter (#1213) * nanointeractive adapter * nanointeractive adapter, changes after review * nanointeractive adapter * nanointeractive adapter, changes after review * formatting --- adapters/nanointeractive/nanointeractive.go | 172 ++++++++++++++++++ .../nanointeractive/nanointeractive_test.go | 10 + .../exemplary/simple-banner.json | 90 +++++++++ .../params/race/banner.json | 3 + .../supplemental/bad_response.json | 63 +++++++ .../supplemental/invalid-params.json | 81 +++++++++ .../supplemental/multi-param.json | 151 +++++++++++++++ .../supplemental/status_204.json | 58 ++++++ .../supplemental/status_400.json | 63 +++++++ .../supplemental/status_418.json | 63 +++++++ adapters/nanointeractive/params_test.go | 63 +++++++ adapters/nanointeractive/usersync.go | 12 ++ adapters/nanointeractive/usersync_test.go | 36 ++++ config/config.go | 2 + exchange/adapter_map.go | 32 ++-- openrtb_ext/bidders.go | 2 + openrtb_ext/imp_nanointeractive.go | 10 + static/bidder-info/nanointeractive.yaml | 9 + static/bidder-params/nanointeractive.json | 32 ++++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 21 files changed, 940 insertions(+), 15 deletions(-) create mode 100644 adapters/nanointeractive/nanointeractive.go create mode 100644 adapters/nanointeractive/nanointeractive_test.go create mode 100644 adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json create mode 100644 adapters/nanointeractive/nanointeractivetest/params/race/banner.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json create mode 100644 adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json create mode 100644 adapters/nanointeractive/params_test.go create mode 100644 adapters/nanointeractive/usersync.go create mode 100644 adapters/nanointeractive/usersync_test.go create mode 100644 openrtb_ext/imp_nanointeractive.go create mode 100644 static/bidder-info/nanointeractive.yaml create mode 100644 static/bidder-params/nanointeractive.json diff --git a/adapters/nanointeractive/nanointeractive.go b/adapters/nanointeractive/nanointeractive.go new file mode 100644 index 00000000000..72832893af1 --- /dev/null +++ b/adapters/nanointeractive/nanointeractive.go @@ -0,0 +1,172 @@ +package nanointeractive + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" +) + +type NanoInteractiveAdapter struct { + endpoint string +} + +func (a *NanoInteractiveAdapter) Name() string { + return "Nano" +} + +func (a *NanoInteractiveAdapter) SkipNoCookies() bool { + return false +} + +func (a *NanoInteractiveAdapter) MakeRequests(bidRequest *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + var errs []error + var validImps []openrtb.Imp + + var adapterRequests []*adapters.RequestData + var referer string = "" + + for i := 0; i < len(bidRequest.Imp); i++ { + + ref, err := checkImp(&bidRequest.Imp[i]) + + // If the parsing is failed, remove imp and add the error. + if err != nil { + errs = append(errs, err) + continue + } + if referer == "" && ref != "" { + referer = ref + } + validImps = append(validImps, bidRequest.Imp[i]) + } + + if len(validImps) == 0 { + errs = append(errs, fmt.Errorf("no impressions in the bid request")) + return nil, errs + } + + // set referer origin + if referer != "" { + if bidRequest.Site == nil { + bidRequest.Site = &openrtb.Site{} + } + bidRequest.Site.Ref = referer + } + + bidRequest.Imp = validImps + + reqJSON, err := json.Marshal(bidRequest) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + if bidRequest.Device != nil { + headers.Add("User-Agent", bidRequest.Device.UA) + headers.Add("X-Forwarded-For", bidRequest.Device.IP) + } + if bidRequest.Site != nil { + headers.Add("Referer", bidRequest.Site.Page) + } + + // set user's cookie + if bidRequest.User != nil && bidRequest.User.BuyerUID != "" { + headers.Add("Cookie", "Nano="+bidRequest.User.BuyerUID) + } + + adapterRequests = append(adapterRequests, &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: reqJSON, + Headers: headers, + }) + + return adapterRequests, errs +} + +func (a *NanoInteractiveAdapter) MakeBids( + internalRequest *openrtb.BidRequest, + externalRequest *adapters.RequestData, + response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } else if response.StatusCode == http.StatusBadRequest { + return nil, []error{adapters.BadInput("Invalid request.")} + } else if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("unexpected HTTP status %d.", response.StatusCode), + }} + } + + var openRtbBidResponse openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &openRtbBidResponse); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("bad server body response"), + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(openRtbBidResponse.SeatBid[0].Bid)) + bidResponse.Currency = openRtbBidResponse.Cur + + sb := openRtbBidResponse.SeatBid[0] + for i := 0; i < len(sb.Bid); i++ { + if !(sb.Bid[i].Price > 0) { + continue + } + bid := sb.Bid[i] + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: openrtb_ext.BidTypeBanner, + }) + } + return bidResponse, nil +} + +func checkImp(imp *openrtb.Imp) (string, error) { + // We support only banner impression + if imp.Banner == nil { + return "", fmt.Errorf("invalid MediaType. NanoInteractive only supports Banner type. ImpID=%s", imp.ID) + } + + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return "", fmt.Errorf("ext not provided; ImpID=%s", imp.ID) + } + + var nanoExt openrtb_ext.ExtImpNanoInteractive + if err := json.Unmarshal(bidderExt.Bidder, &nanoExt); err != nil { + return "", fmt.Errorf("ext.bidder not provided; ImpID=%s", imp.ID) + } + if nanoExt.Pid == "" { + return "", fmt.Errorf("pid is empty; ImpID=%s", imp.ID) + } + + if nanoExt.Ref != "" { + return string(nanoExt.Ref), nil + } + + return "", nil +} + +func NewNanoIneractiveBidder(endpoint string) *NanoInteractiveAdapter { + return &NanoInteractiveAdapter{ + endpoint: endpoint, + } +} + +func NewNanoInteractiveAdapter(uri string) *NanoInteractiveAdapter { + return &NanoInteractiveAdapter{ + endpoint: uri, + } +} diff --git a/adapters/nanointeractive/nanointeractive_test.go b/adapters/nanointeractive/nanointeractive_test.go new file mode 100644 index 00000000000..fa7069a5da3 --- /dev/null +++ b/adapters/nanointeractive/nanointeractive_test.go @@ -0,0 +1,10 @@ +package nanointeractive + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "nanointeractivetest", NewNanoIneractiveBidder("https://ad.audiencemanager.de/hbs")) +} diff --git a/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json b/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json new file mode 100644 index 00000000000..20cc70b6785 --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{ "w": 300,"h": 250} + ] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "nanointeractive", + "bid": [{ + "id": "1", + "impid": "test-imp-id", + "price": 0.4580126, + "adm": "", + "adid": "test_ad_id", + "adomain": ["audiencemanager.de"], + "cid": "test_cid", + "crid": "test_banner_crid", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5a7789eg2662b524d8d7264a96", + "cur": "EUR" + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "price": 0.4580126, + "adm": "", + "adid": "test_ad_id", + "adomain": ["yahoo.com"], + "cid": "test_cid", + "crid": "test_banner_crid", + "h": 250, + "w": 300 + }, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/params/race/banner.json b/adapters/nanointeractive/nanointeractivetest/params/race/banner.json new file mode 100644 index 00000000000..bb35ea8488a --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "pid": "58bfec94eb0a1916fa380163" +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json b/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json new file mode 100644 index 00000000000..587c952a042 --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "213" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "213" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": "{\"id\"data.lost" + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "bad server body response", + "comparison": "literal" + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json b/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json new file mode 100644 index 00000000000..631dc99e5a8 --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json @@ -0,0 +1,81 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": {}, + "ext": { + "bidder": {} + } + }, + { + "id": "test-imp-id-2", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [{"w": 300, "h": 250}] + } + }, + { + "id": "test-imp-id-4", + "video": {}, + "ext": { + "bidder": {} + } + }, + { + "id": "test-imp-id-5", + "audio": { + "startdelay": 0, + "api": [] + }, + "ext": { + "bidder": {} + } + } + ], + "site": { + "id": "siteID", + "publisher": { + "id": "1234" + } + }, + "device": { + "os": "android" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "pid is empty; ImpID=test-imp-id-1", + "comparison": "literal" + }, + { + "value": "ext.bidder not provided; ImpID=test-imp-id-2", + "comparison": "literal" + }, + { + "value": "ext not provided; ImpID=test-imp-id-3", + "comparison": "literal" + }, + { + "value": "invalid MediaType. NanoInteractive only supports Banner type. ImpID=test-imp-id-4", + "comparison": "literal" + }, + { + "value": "invalid MediaType. NanoInteractive only supports Banner type. ImpID=test-imp-id-5", + "comparison": "literal" + }, + { + "value": "no impressions in the bid request", + "comparison": "literal" + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json b/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json new file mode 100644 index 00000000000..27e7bec1f5f --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json @@ -0,0 +1,151 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163", + "ref": "https://nanointeractive.com" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163", + "nq": ["search query"], + "category": "Automotive", + "subId": "a23", + "ref": "https://nanointeractive.com" + } + } + } + ], + "device": { + "ip": "127.0.0.1", + "ua": "user_agent" + }, + "user": { + "buyeruid": "userId" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{ "w": 300,"h": 250} + ] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163", + "ref": "https://nanointeractive.com" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [{ "w": 300,"h": 250} + ] + }, + "ext": { + "bidder": { + "pid": "58bfec94eb0a1916fa380163", + "nq": ["search query"], + "category": "Automotive", + "subId": "a23", + "ref": "https://nanointeractive.com" + } + } + } + ], + "site": { + "ref": "https://nanointeractive.com" + }, + "device": { + "ip": "127.0.0.1", + "ua": "user_agent" + }, + "user": { + "buyeruid": "userId" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "nanointeractive", + "bid": [{ + "id": "1", + "impid": "test-imp-id", + "price": 0.4580126, + "adm": "", + "adid": "test_ad_id", + "adomain": ["audiencemanager.de"], + "cid": "test_cid", + "crid": "test_banner_crid", + "h": 250, + "w": 300 + },{ + "id": "2", + "impid": "test-imp-id2", + "price": 0, + "adm": "", + "adid": "test_ad_id", + "adomain": ["audiencemanager.de"], + "cid": "test_cid", + "crid": "test_banner_crid", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5a7789eg2662b524d8d7264a96", + "cur": "EUR" + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "price": 0.4580126, + "adm": "", + "adid": "test_ad_id", + "adomain": ["yahoo.com"], + "cid": "test_cid", + "crid": "test_banner_crid", + "h": 250, + "w": 300 + }, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json new file mode 100644 index 00000000000..ed4d8ff38b8 --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json new file mode 100644 index 00000000000..f02bd478656 --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Invalid request.", + "comparison": "literal" + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json new file mode 100644 index 00000000000..b7ed65da2af --- /dev/null +++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ad.audiencemanager.de/hbs", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "pid": "123" + } + } + } + ] + } + }, + "mockResponse": { + "status": 418, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "unexpected HTTP status 418.", + "comparison": "literal" + } + ] +} diff --git a/adapters/nanointeractive/params_test.go b/adapters/nanointeractive/params_test.go new file mode 100644 index 00000000000..b290f3d94b1 --- /dev/null +++ b/adapters/nanointeractive/params_test.go @@ -0,0 +1,63 @@ +package nanointeractive + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/nanointeractive.json +// +// These also validate the format of the external API: request.imp[i].ext.nanointeracive + +// TestValidParams makes sure that the NanoInteractive schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderNanoInteractive, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected NanoInteractive params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the Marsmedia schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderNanoInteractive, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"pid": "dafad098"}`, + `{"pid":"dfasfda","nq":["search query"]}`, + `{"pid":"dfasfda","nq":["search query"],"subId":"any string value","category":"any string value"}`, +} + +var invalidParams = []string{ + `{"pid":123}`, + `{"pid":"12323","nq":"search query not an array"}`, + `{"pid":"12323","category":1}`, + `{"pid":"12323","subId":23}`, + ``, + `null`, + `true`, + `9`, + `1.2`, + `[]`, + `{}`, + `placementId`, + `zone`, + `zoneId`, +} diff --git a/adapters/nanointeractive/usersync.go b/adapters/nanointeractive/usersync.go new file mode 100644 index 00000000000..e6227436fb2 --- /dev/null +++ b/adapters/nanointeractive/usersync.go @@ -0,0 +1,12 @@ +package nanointeractive + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewNanoInteractiveSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("nanointeractive", 72, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go new file mode 100644 index 00000000000..ec9787bc20d --- /dev/null +++ b/adapters/nanointeractive/usersync_test.go @@ -0,0 +1,36 @@ +package nanointeractive + +import ( + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/stretchr/testify/assert" +) + +func TestNewNanoInteractiveSyncer(t *testing.T) { + syncURL := "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + userSync := NewNanoInteractiveSyncer(syncURLTemplate) + syncInfo, err := userSync.GetUsersyncInfo( + privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + }, + CCPA: ccpa.Policy{ + Value: "1NYN", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr=1&consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24UID", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 72, userSync.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index d4edab2b53f..999b1870b54 100644 --- a/config/config.go +++ b/config/config.go @@ -521,6 +521,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderNanoInteractive, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderOpenx, "https://rtb.openx.net/sync/prebid?r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25") @@ -713,6 +714,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet") v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/") + v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs") v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid") v.SetDefault("adapters.pubmatic.endpoint", "https://hbopenbid.pubmatic.com/translator?source=prebid-server") v.SetDefault("adapters.pubnative.endpoint", "http://dsp.pubnative.net/bid/v1/request") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 05f44e24b66..f7b970c571b 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -39,6 +39,7 @@ import ( "github.com/prebid/prebid-server/adapters/lockerdome" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" + "github.com/prebid/prebid-server/adapters/nanointeractive" "github.com/prebid/prebid-server/adapters/openx" "github.com/prebid/prebid-server/adapters/pubmatic" "github.com/prebid/prebid-server/adapters/pubnative" @@ -96,21 +97,22 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret), - openrtb_ext.BidderGamma: gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint), - openrtb_ext.BidderGamoshi: gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint), - openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint), - openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), - openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint), - openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint), - openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), - openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), - openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), - openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), - openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint), - openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint), - openrtb_ext.BidderPubnative: pubnative.NewPubnativeBidder(cfg.Adapters[string(openrtb_ext.BidderPubnative)].Endpoint), - openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint), - openrtb_ext.BidderRTBHouse: rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint), + openrtb_ext.BidderGamma: gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint), + openrtb_ext.BidderGamoshi: gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint), + openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint), + openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), + openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint), + openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint), + openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), + openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), + openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), + openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), + openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint), + openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint), + openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint), + openrtb_ext.BidderPubnative: pubnative.NewPubnativeBidder(cfg.Adapters[string(openrtb_ext.BidderPubnative)].Endpoint), + openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint), + openrtb_ext.BidderRTBHouse: rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint), openrtb_ext.BidderRubicon: rubicon.NewRubiconBidder( client, cfg.Adapters[string(openrtb_ext.BidderRubicon)].Endpoint, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index e3f186db333..ec9745563ef 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -57,6 +57,7 @@ const ( BidderLockerDome BidderName = "lockerdome" BidderMarsmedia BidderName = "marsmedia" BidderMgid BidderName = "mgid" + BidderNanoInteractive BidderName = "nanointeractive" BidderOpenx BidderName = "openx" BidderPubmatic BidderName = "pubmatic" BidderPubnative BidderName = "pubnative" @@ -119,6 +120,7 @@ var BidderMap = map[string]BidderName{ "lockerdome": BidderLockerDome, "marsmedia": BidderMarsmedia, "mgid": BidderMgid, + "nanointeractive": BidderNanoInteractive, "openx": BidderOpenx, "pubmatic": BidderPubmatic, "pubnative": BidderPubnative, diff --git a/openrtb_ext/imp_nanointeractive.go b/openrtb_ext/imp_nanointeractive.go new file mode 100644 index 00000000000..28db5be0d07 --- /dev/null +++ b/openrtb_ext/imp_nanointeractive.go @@ -0,0 +1,10 @@ +package openrtb_ext + +// ExtImpNanoInteractive defines the contract for bidrequest.imp[i].ext.nanointeractive +type ExtImpNanoInteractive struct { + Pid string `json:"pid"` + Nq []string `json:"nq, omitempty"` + Category string `json:"category, omitempty"` + SubId string `json:"subId, omitempty"` + Ref string `json:"ref, omitempty"` +} diff --git a/static/bidder-info/nanointeractive.yaml b/static/bidder-info/nanointeractive.yaml new file mode 100644 index 00000000000..244e7602950 --- /dev/null +++ b/static/bidder-info/nanointeractive.yaml @@ -0,0 +1,9 @@ +maintainer: + email: "development@nanointeractive.com" +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner diff --git a/static/bidder-params/nanointeractive.json b/static/bidder-params/nanointeractive.json new file mode 100644 index 00000000000..707dff2fa50 --- /dev/null +++ b/static/bidder-params/nanointeractive.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "NanoInteractive Adapter Params", + "description": "A schema which validates params accepted by the NanoInteractive adapter", + "type": "object", + "properties": { + "pid": { + "type": "string", + "description": "Placement idd" + }, + "nq": { + "type": "array", + "items": { + "type": "string" + }, + "description": "search queries" + }, + "category": { + "type": "string", + "description": "IAB Category" + }, + "subId": { + "type": "string", + "description": "any segment value provided by publisher" + }, + "ref" : { + "type": "string", + "description": "referer" + } + }, + "required": ["pid"] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index c7ad70b7eff..be0392f2dbb 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -34,6 +34,7 @@ import ( "github.com/prebid/prebid-server/adapters/lockerdome" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" + "github.com/prebid/prebid-server/adapters/nanointeractive" "github.com/prebid/prebid-server/adapters/openx" "github.com/prebid/prebid-server/adapters/pubmatic" "github.com/prebid/prebid-server/adapters/pulsepoint" @@ -96,6 +97,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMarsmedia, marsmedia.NewMarsmediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderNanoInteractive, nanointeractive.NewNanoInteractiveSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderOpenx, openx.NewOpenxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderPubmatic, pubmatic.NewPubmaticSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderPulsepoint, pulsepoint.NewPulsepointSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 3de64ec1eb0..383e24d82cf 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -43,6 +43,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderLockerDome): syncConfig, string(openrtb_ext.BidderMarsmedia): syncConfig, string(openrtb_ext.BidderMgid): syncConfig, + string(openrtb_ext.BidderNanoInteractive): syncConfig, string(openrtb_ext.BidderOpenx): syncConfig, string(openrtb_ext.BidderPubmatic): syncConfig, string(openrtb_ext.BidderPulsepoint): syncConfig, From fb386190f4491648bb1e8d1b0345a333be1c0393 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 6 Apr 2020 18:53:03 -0400 Subject: [PATCH 045/318] Typos Fix (#1236) * Fix Typo * Fixed More Typos --- adapters/adapterstest/test_json.go | 2 +- analytics/config/config_test.go | 6 +++--- config/config.go | 2 +- config/config_test.go | 4 ++-- config/stored_requests.go | 2 +- docs/bidders/appnexus.md | 2 +- docs/bidders/audienceNetwork.md | 2 +- docs/bidders/sovrn.md | 2 +- docs/developers/automated-tests.md | 2 +- docs/developers/cookie-syncs.md | 2 +- docs/developers/default-request.md | 6 +++--- docs/endpoints/openrtb2/amp.md | 2 +- docs/endpoints/openrtb2/auction.md | 8 +++---- endpoints/openrtb2/amp_auction_test.go | 10 ++++----- endpoints/openrtb2/auction_test.go | 10 ++++----- endpoints/openrtb2/video_auction_test.go | 6 +++--- exchange/bidder.go | 2 +- exchange/exchange_test.go | 2 +- gdpr/gdpr.go | 6 +++--- main.go | 4 +++- openrtb_ext/bid.go | 2 +- openrtb_ext/request.go | 4 ++-- openrtb_ext/request_test.go | 8 +++---- pbsmetrics/metrics.go | 2 +- pbsmetrics/prometheus/prometheus.go | 4 ++-- pbsmetrics/prometheus/prometheus_test.go | 6 +++--- .../aspects/request_timeout_handler_test.go | 21 ++++++++++--------- ssl/ssl_test.go | 2 +- .../backends/db_fetcher/fetcher.go | 2 +- stored_requests/events/events_test.go | 2 +- stored_requests/events/http/http.go | 2 +- stored_requests/fetcher.go | 2 +- 32 files changed, 71 insertions(+), 68 deletions(-) diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go index a0d1954894a..7602ab16e41 100644 --- a/adapters/adapterstest/test_json.go +++ b/adapters/adapterstest/test_json.go @@ -301,7 +301,7 @@ func diffJson(t *testing.T, description string, actual []byte, expected []byte) if diff.Modified() { var left interface{} if err := json.Unmarshal(actual, &left); err != nil { - t.Fatalf("%s json did not match, but unmarhsalling failed. %v", description, err) + t.Fatalf("%s json did not match, but unmarshalling failed. %v", description, err) } printer := formatter.NewAsciiFormatter(left, formatter.AsciiFormatterConfig{ ShowArrayIndex: true, diff --git a/analytics/config/config_test.go b/analytics/config/config_test.go index 0fd3ec2019e..7d97fb5f1be 100644 --- a/analytics/config/config_test.go +++ b/analytics/config/config_test.go @@ -22,7 +22,7 @@ func TestSampleModule(t *testing.T) { Response: &openrtb.BidResponse{}, }) if count != 1 { - t.Errorf("PBSAnalyticsModule failed at LogAuctionObejct") + t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") } am.LogSetUIDObject(&analytics.SetUIDObject{ @@ -33,12 +33,12 @@ func TestSampleModule(t *testing.T) { Success: true, }) if count != 2 { - t.Errorf("PBSAnalyticsModule failed at LogSetUIDObejct") + t.Errorf("PBSAnalyticsModule failed at LogSetUIDObject") } am.LogCookieSyncObject(&analytics.CookieSyncObject{}) if count != 3 { - t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObejct") + t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObject") } am.LogAmpObject(&analytics.AmpObject{}) diff --git a/config/config.go b/config/config.go index 999b1870b54..2cb5f8f2e66 100644 --- a/config/config.go +++ b/config/config.go @@ -221,7 +221,7 @@ const ( type Adapter struct { Endpoint string `mapstructure:"endpoint"` // Required // UserSyncURL is the URL returned by /cookie_sync for this Bidder. It is _usually_ optional. - // If not defined, sensible defaults will be derved based on the config.external_url. + // If not defined, sensible defaults will be derived based on the config.external_url. // Note that some Bidders don't have sensible defaults, because their APIs require an ID that will vary // from one PBS host to another. // diff --git a/config/config_test.go b/config/config_test.go index 78630e071d9..9677ce2aaba 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -417,9 +417,9 @@ func TestCookieSizeError(t *testing.T) { } for i := range testCases { if testCases[i].expectError { - assert.Error(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCooki.MaxCookieSizeBytes less than MIN_COOKIE_SIZE_BYTES = %d and not equal to zero should return an error", MIN_COOKIE_SIZE_BYTES)) + assert.Error(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCookie.MaxCookieSizeBytes less than MIN_COOKIE_SIZE_BYTES = %d and not equal to zero should return an error", MIN_COOKIE_SIZE_BYTES)) } else { - assert.NoError(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCooki.MaxCookieSizeBytes greater than MIN_COOKIE_SIZE_BYTES = %d or equal to zero should not return an error", MIN_COOKIE_SIZE_BYTES)) + assert.NoError(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCookie.MaxCookieSizeBytes greater than MIN_COOKIE_SIZE_BYTES = %d or equal to zero should not return an error", MIN_COOKIE_SIZE_BYTES)) } } } diff --git a/config/stored_requests.go b/config/stored_requests.go index 0d9e773205e..04e400f9b7c 100644 --- a/config/stored_requests.go +++ b/config/stored_requests.go @@ -402,7 +402,7 @@ func (cfg *PostgresUpdatePolling) validate(errs configErrors) configErrors { return errs } -// MakeQuery builds a query which can fetch numReqs Stored Requetss and numImps Stored Imps. +// MakeQuery builds a query which can fetch numReqs Stored Requests and numImps Stored Imps. // See the docs on PostgresConfig.QueryTemplate for a description of how it works. func (cfg *PostgresFetcherQueriesSlim) MakeQuery(numReqs int, numImps int) (query string) { return resolve(cfg.QueryTemplate, numReqs, numImps) diff --git a/docs/bidders/appnexus.md b/docs/bidders/appnexus.md index 8b706adc122..e4032313f25 100644 --- a/docs/bidders/appnexus.md +++ b/docs/bidders/appnexus.md @@ -15,7 +15,7 @@ The AppNexus endpoint expects `imp.displaymanagerver` to be populated for mobile requests, however not all SDKs will populate this field. If the `imp.displaymanagerver` field is not supplied for an `imp`, but `request.app.ext.prebid.source` and `request.app.ext.prebid.version` are supplied, the adapter will fill in a value for -`diplaymanagerver`. It will concatonate the two `app` fields as `-` fo fill in +`diplaymanagerver`. It will concatenate the two `app` fields as `-` fo fill in the empty `displaymanagerver` before sending the request to AppNexus. ## Test Request diff --git a/docs/bidders/audienceNetwork.md b/docs/bidders/audienceNetwork.md index 04357d616b1..d55e8218a81 100644 --- a/docs/bidders/audienceNetwork.md +++ b/docs/bidders/audienceNetwork.md @@ -3,6 +3,6 @@ ## Mobile Bids Audience Network will not bid on requests made from device simulators. -When testingfor Mobile bids, you must make bid requests using a real device. +When testing for Mobile bids, you must make bid requests using a real device. **Note:** Audience Network is disabled by default. Please enable it in the app config if you wish to use it. Make sure you provide the partnerID for the auctions to run correctly. \ No newline at end of file diff --git a/docs/bidders/sovrn.md b/docs/bidders/sovrn.md index 544cb8a6764..bc6d42333e8 100644 --- a/docs/bidders/sovrn.md +++ b/docs/bidders/sovrn.md @@ -1,3 +1,3 @@ Sovrn supports 2 parameters to be present in the `ext` object of impressions sent to it: - tagid: a string containing the sovrn-specific id(s) for the publisher's ad tag(s) they would like to bid with. This is a required field -- bidfloor: The minimium acceptable bid, in CPM, using US Dollars. This is an optional field. \ No newline at end of file +- bidfloor: The minimum acceptable bid, in CPM, using US Dollars. This is an optional field. \ No newline at end of file diff --git a/docs/developers/automated-tests.md b/docs/developers/automated-tests.md index 12532237e08..0dff9b04212 100644 --- a/docs/developers/automated-tests.md +++ b/docs/developers/automated-tests.md @@ -9,7 +9,7 @@ To reproduce these tests locally, use: ## Writing Tests -Tests for `some-file.go` should be placed in the file `some-file_test.go` in the same paackage. +Tests for `some-file.go` should be placed in the file `some-file_test.go` in the same package. For more info on how to write tests in Go, see [the Go docs](https://golang.org/pkg/testing/). ## Adapter Tests diff --git a/docs/developers/cookie-syncs.md b/docs/developers/cookie-syncs.md index 36c6b85b636..75a3e3b0ef8 100644 --- a/docs/developers/cookie-syncs.md +++ b/docs/developers/cookie-syncs.md @@ -1,6 +1,6 @@ # Cookie Sync Technical Details -This document describes the mechancis of a Prebid Server cookie sync. +This document describes the mechanics of a Prebid Server cookie sync. ## Motivation diff --git a/docs/developers/default-request.md b/docs/developers/default-request.md index 2337ccd8da0..f071d91bad6 100644 --- a/docs/developers/default-request.md +++ b/docs/developers/default-request.md @@ -1,6 +1,6 @@ # Server Based Global Default Request -This allows a defaut stored request to be defined that allows the server to set up some defaults for all incoming requests. A request specified stored request will override these defaults, and of course any options specified directly in the stored request override both. The default stored request is only read on server startup, it is meant as an installation static default rather than a dynamic tuning option. +This allows a default stored request to be defined that allows the server to set up some defaults for all incoming requests. A request specified stored request will override these defaults, and of course any options specified directly in the stored request override both. The default stored request is only read on server startup, it is meant as an installation static default rather than a dynamic tuning option. A common use case is to "hard code" aliases into the server. This saves having to specify them on all incoming requests, and/or on all stored requests. To help support automation and alias discovery we can flag that any aliases found in the file be added to the bidder info endpoints. @@ -35,8 +35,8 @@ The `filename` option is the path/filename of a JSON file containing the default ``` This will be JSON merged into the incoming requests at the top level. These will be used as fallbacks which can be overridden by both Stored Requests _and_ the incoming HTTP request payload. -The `info` option determines if the alised bidders will be exposed on the `/info` endpoints. If true the alias name will be added to the list returned by -`/info/bidders` and the info JSON for the core bidder will be coppied into `/info/bidder/{biddername}` with the addition of the field +The `info` option determines if the aliased bidders will be exposed on the `/info` endpoints. If true the alias name will be added to the list returned by +`/info/bidders` and the info JSON for the core bidder will be copied into `/info/bidder/{biddername}` with the addition of the field `"alias_of": "{coreBidder}"` to indicate that it is an aliases, and of which core bidder. Turning the info support on may be useful for hosts that want to support automation around the `/info` endpoints that will include the predefined aliases. This config option may be deprecated in a future version to promote a consistency in the endpoint functionality, depending on the perceived need for the option. diff --git a/docs/endpoints/openrtb2/amp.md b/docs/endpoints/openrtb2/amp.md index b792ae6ec5d..16fa451ef36 100644 --- a/docs/endpoints/openrtb2/amp.md +++ b/docs/endpoints/openrtb2/amp.md @@ -100,7 +100,7 @@ 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_timeout_adjustment_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. `debug` - When set to `1`, the respones will contain extra info for debugging. +8. `debug` - When set to `1`, the response will contain extra info for debugging. 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). diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index bd421850d1f..67430e51481 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -228,7 +228,7 @@ for each Bidder by using the `/cookie_sync` endpoint, and calling the URLs that #### Native Request -For each native request, the `assets` objects's `id` field must not be defined. Prebid Server will set this automatically, using the index of the asset in the array as the ID. +For each native request, the `assets` object's `id` field must not be defined. Prebid Server will set this automatically, using the index of the asset in the array as the ID. #### Bidder Aliases @@ -265,7 +265,7 @@ This can be used to request bids from the same Bidder with different params. For ``` For all intents and purposes, the alias will be treated as another Bidder. This new Bidder will behave exactly -like the original, except that the Response will contain seprate SeatBids, and any Targeting keys +like the original, except that the Response will contain separate SeatBids, and any Targeting keys will be formed using the alias' name. If an alias overlaps with a core Bidder's name, then the alias will take precedence. @@ -280,7 +280,7 @@ For example, if the Request defines an alias like this: ``` then any `imp.ext.appnexus` params will actually go to the **rubicon** adapter. -It will become impossible to fetch bids from Appnexus within that Request. +It will become impossible to fetch bids from AppNexus within that Request. #### Bidder Response Times @@ -495,7 +495,7 @@ client can declare a given adunit as eligible for rewards by declaring `imp.ext. While testing SDK and video integrations, it's important, but often difficult, to get consistent responses back from bidders that cover a range of scenarios like different CPM values, deals, etc. Prebid Server supports a debugging workflow in two ways: - a stored-auction-response that covers multiple bidder responses -- multiple stored-bid-reponses at the bidder adapter level +- multiple stored-bid-responses at the bidder adapter level **Single Stored Auction Response ID** diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index b25d5b0cc8f..9dc81eb1b9d 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -219,7 +219,7 @@ func TestGDPRConsent(t *testing.T) { responseRecorder := httptest.NewRecorder() endpoint(responseRecorder, request, nil) - // Parse Resonse + // Parse Response var response AmpResponse if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) @@ -372,7 +372,7 @@ func TestCCPAConsent(t *testing.T) { responseRecorder := httptest.NewRecorder() endpoint(responseRecorder, request, nil) - // Parse Resonse + // Parse Response var response AmpResponse if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) @@ -431,7 +431,7 @@ func TestNoConsent(t *testing.T) { responseRecorder := httptest.NewRecorder() endpoint(responseRecorder, request, nil) - // Parse Resonse + // Parse Response var response AmpResponse if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) @@ -478,7 +478,7 @@ func TestInvalidConsent(t *testing.T) { responseRecorder := httptest.NewRecorder() endpoint(responseRecorder, request, nil) - // Parse Resonse + // Parse Response var response AmpResponse if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) @@ -561,7 +561,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) { responseRecorder := httptest.NewRecorder() endpoint(responseRecorder, request, nil) - // Parse Resonse + // Parse Response var response AmpResponse if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 74a70c69415..98dfa66d6a4 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -175,7 +175,7 @@ func TestBadNativeRequests(t *testing.T) { tests.assert(t) } -// TestAliasedRequests makes sure we handle (defuault) aliased bidders properly +// TestAliasedRequests makes sure we handle (default) aliased bidders properly func TestAliasedRequests(t *testing.T) { tests := &getResponseFromDirectory{ dir: "sample-requests/aliased", @@ -289,7 +289,7 @@ func (gr *getResponseFromDirectory) assert(t *testing.T) { filesToAssert = append(filesToAssert, gr.dir+"/"+fileInfo.Name()) } } else { - // Just test the single `gr.file`, and not the entiriety of files that may be found in `gr.dir` + // Just test the single `gr.file`, and not the entirety of files that may be found in `gr.dir` filesToAssert = append(filesToAssert, gr.dir+"/"+gr.file) } @@ -805,7 +805,7 @@ func TestDisabledBidder(t *testing.T) { }, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{"unknownbidder": "The biddder 'unknownbidder' has been disabled."}, + map[string]string{"unknownbidder": "The bidder 'unknownbidder' has been disabled."}, false, []byte{}, openrtb_ext.BidderMap, @@ -839,7 +839,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { &config.Configuration{MaxRequestSize: int64(8096)}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{"unknownbidder": "The biddder 'unknownbidder' has been disabled."}, + map[string]string{"unknownbidder": "The bidder 'unknownbidder' has been disabled."}, false, []byte{}, openrtb_ext.BidderMap, @@ -847,7 +847,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { } errs := deps.validateImpExt(imp, nil, 0) assert.JSONEq(t, `{"appnexus":{"placement_id":555}}`, string(imp.Ext)) - assert.Equal(t, []error{&errortypes.BidderTemporarilyDisabled{Message: "The biddder 'unknownbidder' has been disabled."}}, errs) + assert.Equal(t, []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, errs) } func TestEffectivePubID(t *testing.T) { diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 0199b43f610..d0ce33de1c4 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -44,7 +44,7 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} if err := json.Unmarshal(respBytes, resp); err != nil { - t.Fatalf("Unable to umarshal response.") + t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") @@ -197,7 +197,7 @@ func TestVideoEndpointDebugQueryTrue(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} if err := json.Unmarshal(respBytes, resp); err != nil { - t.Fatalf("Unable to umarshal response.") + t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") @@ -239,7 +239,7 @@ func TestVideoEndpointDebugQueryFalse(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} if err := json.Unmarshal(respBytes, resp); err != nil { - t.Fatalf("Unable to umarshal response.") + t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") diff --git a/exchange/bidder.go b/exchange/bidder.go index 97f64e74bb5..8e95835ffba 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -208,7 +208,7 @@ func addNativeTypes(bid *openrtb.Bid, request *openrtb.BidRequest) (*nativeRespo var errs []error var nativeMarkup *nativeResponse.Response if err := json.Unmarshal(json.RawMessage(bid.AdM), &nativeMarkup); err != nil || len(nativeMarkup.Assets) == 0 { - // Some bidders are returning non-IAB complaiant native markup. In this case Prebid server will not be able to add types. E.g Facebook + // Some bidders are returning non-IAB compliant native markup. In this case Prebid server will not be able to add types. E.g Facebook return nil, errs } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 7217e609189..2f115ca4f93 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1762,7 +1762,7 @@ func diffJson(t *testing.T, description string, actual []byte, expected []byte) if diff.Modified() { var left interface{} if err := json.Unmarshal(actual, &left); err != nil { - t.Fatalf("%s json did not match, but unmarhsalling failed. %v", description, err) + t.Fatalf("%s json did not match, but unmarshalling failed. %v", description, err) } printer := formatter.NewAsciiFormatter(left, formatter.AsciiFormatterConfig{ ShowArrayIndex: true, diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index a6b64203a95..4e36e22fdb9 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -12,17 +12,17 @@ import ( type Permissions interface { // Determines whether or not the host company is allowed to read/write cookies. // - // If the consent string was nonsenical, the returned error will be an ErrorMalformedConsent. + // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. HostCookiesAllowed(ctx context.Context, consent string) (bool, error) // Determines whether or not the given bidder is allowed to user personal info for ad targeting. // - // If the consent string was nonsenical, the returned error will be an ErrorMalformedConsent. + // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) // Determines whether or not to send PI information to a bidder, or mask it out. // - // If the consent string was nonsenical, the returned error will be an ErrorMalformedConsent. + // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) } diff --git a/main.go b/main.go index ae3b7fd5705..d6ba430f059 100644 --- a/main.go +++ b/main.go @@ -42,9 +42,11 @@ func main() { } } +const configFileName = "pbs" + func loadConfig() (*config.Configuration, error) { v := viper.New() - config.SetupViper(v, "pbs") // filke = filename + config.SetupViper(v, configFileName) return config.New(v) } diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go index 768128c96d6..3b297c7ab5d 100644 --- a/openrtb_ext/bid.go +++ b/openrtb_ext/bid.go @@ -87,7 +87,7 @@ const ( HbpbConstantKey TargetingKey = "hb_pb" // HbEnvKey exists to support the Prebid Universal Creative. If it exists, the only legal value is mobile-app. - // It will exist only if the incoming bidRequest defiend request.app instead of request.site. + // It will exist only if the incoming bidRequest defined request.app instead of request.site. HbEnvKey TargetingKey = "hb_env" // HbCacheHost and HbCachePath exist to supply cache host and path as targeting parameters diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 9d1456c9618..25b5c881408 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -160,7 +160,7 @@ func (pg *PriceGranularity) UnmarshalJSON(b []byte) error { func PriceGranularityFromString(gran string) PriceGranularity { switch gran { case "low": - return priceGranulrityLow + return priceGranularityLow case "med", "medium": // Seems that PBS was written with medium = "med", so hacking that in return priceGranularityMed @@ -175,7 +175,7 @@ func PriceGranularityFromString(gran string) PriceGranularity { return PriceGranularity{} } -var priceGranulrityLow = PriceGranularity{ +var priceGranularityLow = PriceGranularity{ Precision: 2, Ranges: []GranularityRange{{ Min: 0, diff --git a/openrtb_ext/request_test.go b/openrtb_ext/request_test.go index 3291c4f9fb2..e4046a622db 100644 --- a/openrtb_ext/request_test.go +++ b/openrtb_ext/request_test.go @@ -8,12 +8,12 @@ import ( "github.com/stretchr/testify/assert" ) -// Test the unmashalling of the prebid extensions and setting default Price Granularity +// Test the unmarshalling of the prebid extensions and setting default Price Granularity func TestExtRequestTargeting(t *testing.T) { extRequest := &ExtRequest{} err := json.Unmarshal([]byte(ext1), extRequest) if err != nil { - t.Errorf("ext1 Unmashall falure: %s", err.Error()) + t.Errorf("ext1 Unmarshall failure: %s", err.Error()) } if extRequest.Prebid.Targeting != nil { t.Error("ext1 Targeting is not nil") @@ -22,7 +22,7 @@ func TestExtRequestTargeting(t *testing.T) { extRequest = &ExtRequest{} err = json.Unmarshal([]byte(ext2), extRequest) if err != nil { - t.Errorf("ext2 Unmashall falure: %s", err.Error()) + t.Errorf("ext2 Unmarshall failure: %s", err.Error()) } if extRequest.Prebid.Targeting == nil { t.Error("ext2 Targeting is nil") @@ -36,7 +36,7 @@ func TestExtRequestTargeting(t *testing.T) { extRequest = &ExtRequest{} err = json.Unmarshal([]byte(ext3), extRequest) if err != nil { - t.Errorf("ext3 Unmashall falure: %s", err.Error()) + t.Errorf("ext3 Unmarshall failure: %s", err.Error()) } if extRequest.Prebid.Targeting == nil { t.Error("ext3 Targeting is nil") diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index aea9735c276..cc836011efa 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -248,7 +248,7 @@ func RequestActions() []RequestAction { // MetricsEngine is a generic interface to record PBS metrics into the desired backend // The first three metrics function fire off once per incoming request, so total metrics -// will equal the total numer of incoming requests. The remaining 5 fire off per outgoing +// will equal the total number of incoming requests. The remaining 5 fire off per outgoing // request to a bidder adapter, so will record a number of hits per incoming request. The // two groups should be consistent within themselves, but comparing numbers between groups // is generally not useful. diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index 7cb80643542..e2b646d5238 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -76,7 +76,7 @@ const ( // NewMetrics initializes a new Prometheus metrics instance with preloaded label values. func NewMetrics(cfg config.PrometheusMetrics) *Metrics { requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} - cacheWriteTimeBuckts := []float64{0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1} + cacheWriteTimeBuckets := []float64{0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1} priceBuckets := []float64{250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000} metrics := Metrics{} @@ -112,7 +112,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "prebidcache_write_time_seconds", "Seconds to write to Prebid Cache labeled by success or failure. Failure timing is limited by Prebid Server enforced timeouts.", []string{successLabel}, - cacheWriteTimeBuckts) + cacheWriteTimeBuckets) metrics.requests = newCounter(cfg, metrics.Registry, "requests", diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index 4cf9676e1d4..f76480f0852 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -571,7 +571,7 @@ func TestAdapterRequestMetrics(t *testing.T) { var totalCount float64 var totalCookieNoCount float64 var totalCookieYesCount float64 - var totalCookieUnknowmCount float64 + var totalCookieUnknownCount float64 var totalHasBidsCount float64 processMetrics(m.adapterRequests, func(m dto.Metric) { isMetricForAdapter := false @@ -597,7 +597,7 @@ func TestAdapterRequestMetrics(t *testing.T) { case string(pbsmetrics.CookieFlagYes): totalCookieYesCount += value case string(pbsmetrics.CookieFlagUnknown): - totalCookieUnknowmCount += value + totalCookieUnknownCount += value } } } @@ -606,7 +606,7 @@ func TestAdapterRequestMetrics(t *testing.T) { assert.Equal(t, test.expectedCount, totalCount, test.description+":total") assert.Equal(t, test.expectedCookieNoCount, totalCookieNoCount, test.description+":cookie=no") assert.Equal(t, test.expectedCookieYesCount, totalCookieYesCount, test.description+":cookie=yes") - assert.Equal(t, test.expectedCookieUnknownCount, totalCookieUnknowmCount, test.description+":cookie=unknown") + assert.Equal(t, test.expectedCookieUnknownCount, totalCookieUnknownCount, test.description+":cookie=unknown") assert.Equal(t, test.expectedHasBidsCount, totalHasBidsCount, test.description+":hasBids") } } diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go index b6e10fd64bf..5283d5d51e7 100644 --- a/router/aspects/request_timeout_handler_test.go +++ b/router/aspects/request_timeout_handler_test.go @@ -1,12 +1,13 @@ package aspects import ( - "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" "net/http" "net/http/httptest" "testing" + "github.com/julienschmidt/httprouter" + "github.com/prebid/prebid-server/config" + "github.com/stretchr/testify/assert" ) @@ -18,7 +19,7 @@ func TestAny(t *testing.T) { reqTimeInQueue string reqTimeOut string setHeaders bool - extectedRespCode int + expectedRespCode int expectedRespCodeMessage string expectedRespBody string expectedRespBodyMessage string @@ -28,7 +29,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "6", reqTimeOut: "5", setHeaders: true, - extectedRespCode: http.StatusRequestTimeout, + expectedRespCode: http.StatusRequestTimeout, expectedRespCodeMessage: "Http response code is incorrect, should be 408", expectedRespBody: "Queued request processing time exceeded maximum", expectedRespBodyMessage: "Body should have error message", @@ -38,7 +39,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "0.9", reqTimeOut: "5", setHeaders: true, - extectedRespCode: http.StatusOK, + expectedRespCode: http.StatusOK, expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", @@ -48,7 +49,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "", reqTimeOut: "", setHeaders: false, - extectedRespCode: http.StatusOK, + expectedRespCode: http.StatusOK, expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", @@ -58,7 +59,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "2", reqTimeOut: "", setHeaders: true, - extectedRespCode: http.StatusOK, + expectedRespCode: http.StatusOK, expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", @@ -68,7 +69,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "test1", reqTimeOut: "test2", setHeaders: true, - extectedRespCode: http.StatusInternalServerError, + expectedRespCode: http.StatusInternalServerError, expectedRespCodeMessage: "Http response code is incorrect, should be 400", expectedRespBody: "Request timeout headers are incorrect (wrong format)", expectedRespBodyMessage: "Body should have error message", @@ -78,7 +79,7 @@ func TestAny(t *testing.T) { reqTimeInQueue: "test1", reqTimeOut: "123", setHeaders: true, - extectedRespCode: http.StatusInternalServerError, + expectedRespCode: http.StatusInternalServerError, expectedRespCodeMessage: "Http response code is incorrect, should be 400", expectedRespBody: "Request timeout headers are incorrect (wrong format)", expectedRespBodyMessage: "Body should have error message", @@ -87,7 +88,7 @@ func TestAny(t *testing.T) { for _, test := range testCases { result := ExecuteAspectRequest(t, test.reqTimeInQueue, test.reqTimeOut, test.setHeaders) - assert.Equal(t, test.extectedRespCode, result.Code, test.expectedRespCodeMessage) + assert.Equal(t, test.expectedRespCode, result.Code, test.expectedRespCodeMessage) assert.Equal(t, test.expectedRespBody, string(result.Body.Bytes()), test.expectedRespBodyMessage) } } diff --git a/ssl/ssl_test.go b/ssl/ssl_test.go index c4c29d149ef..b72fb7ae9a3 100644 --- a/ssl/ssl_test.go +++ b/ssl/ssl_test.go @@ -38,7 +38,7 @@ func TestCertsFromFilePoolDontExist(t *testing.T) { // Assert loaded certificates by looking at the length of the subjects array of strings assert.NoError(t, err, "Error thrown by AppendPEMFileToRootCAPool while loading file %s: %v", certificatesFile, err) subjects := certPool.Subjects() - assert.Equal(t, len(subjects), 1, "We only loaded one vertificate from the file, len(subjects) should equal 1") + assert.Equal(t, len(subjects), 1, "We only loaded one certificate from the file, len(subjects) should equal 1") } func TestAppendPEMFileToRootCAPoolFail(t *testing.T) { diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go index a8232fd5173..223067c917e 100644 --- a/stored_requests/backends/db_fetcher/fetcher.go +++ b/stored_requests/backends/db_fetcher/fetcher.go @@ -113,7 +113,7 @@ func appendErrors(dataType string, ids []string, data map[string]json.RawMessage // // These errors are documented here: https://www.postgresql.org/docs/9.3/static/errcodes-appendix.html func isBadInput(err error) bool { - // Unfortunately, Postgres queries will fail if a non-UUID is passedd into a query for a UUID column. For example: + // Unfortunately, Postgres queries will fail if a non-UUID is passed into a query for a UUID column. For example: // // SELECT uuid, data, dataType FROM stored_requests WHERE uuid IN ('abc'); // diff --git a/stored_requests/events/events_test.go b/stored_requests/events/events_test.go index aaece692bd2..240a697592a 100644 --- a/stored_requests/events/events_test.go +++ b/stored_requests/events/events_test.go @@ -23,7 +23,7 @@ func TestListen(t *testing.T) { TTL: -1, }) - // create channels to syncronize + // create channels to synchronize saveOccurred := make(chan struct{}) invalidateOccurred := make(chan struct{}) listener := NewEventListener( diff --git a/stored_requests/events/http/http.go b/stored_requests/events/http/http.go index 4f141dac5cd..a6a129eed42 100644 --- a/stored_requests/events/http/http.go +++ b/stored_requests/events/http/http.go @@ -142,7 +142,7 @@ func (e *HTTPEvents) refresh(ticker <-chan time.Time) { } } -// proceess unpacks the HTTP response and sends the relevant events to the channels. +// parse unpacks the HTTP response and sends the relevant events to the channels. // It returns true if everything was successful, and false if any errors occurred. func (e *HTTPEvents) parse(endpoint string, resp *httpCore.Response, err error) (*responseContract, bool) { if err != nil { diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go index 808495e4584..23fdb6b4925 100644 --- a/stored_requests/fetcher.go +++ b/stored_requests/fetcher.go @@ -30,7 +30,7 @@ type CategoryFetcher interface { FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) } -// AllFetcher is an iterface that encapsulates both the original Fetcher and the CategoryFetcher +// AllFetcher is an interface that encapsulates both the original Fetcher and the CategoryFetcher type AllFetcher interface { FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) From 1af4a6af23d2b808c383f5a193b8809a1b7cb016 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Thu, 9 Apr 2020 07:44:38 -0700 Subject: [PATCH 046/318] Moved hb_pc_cat_dur modification to be before caching (#1250) --- exchange/exchange.go | 20 +++++++++++--------- exchange/exchange_test.go | 18 ++++++++++++------ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index 3cab1880456..e625e5ca8f3 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -179,6 +179,12 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque errs = append(errs, errors.New("Unable to marshal response ext for debugging")) } } + + if requestExt.Prebid.SupportDeals { + dealErrs := applyDealSupport(bidRequest, auc, bidCategory) + errs = append(errs, dealErrs...) + } + cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &e.defaultTTLs, bidCategory, debugLog) if len(cacheErrs) > 0 { errs = append(errs, cacheErrs...) @@ -186,10 +192,6 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque targData.setTargeting(auc, bidRequest.App != nil, bidCategory) } - if requestExt.Prebid.SupportDeals { - dealErrs := applyDealSupport(bidRequest, auc) - errs = append(errs, dealErrs...) - } } // Build the response @@ -210,7 +212,7 @@ type BidderDealTier struct { } // applyDealSupport updates targeting keys with deal prefixes if minimum deal tier exceeded -func applyDealSupport(bidRequest *openrtb.BidRequest, auc *auction) []error { +func applyDealSupport(bidRequest *openrtb.BidRequest, auc *auction, bidCategory map[string]string) []error { errs := []error{} impDealMap := getDealTiers(bidRequest) @@ -221,7 +223,7 @@ func applyDealSupport(bidRequest *openrtb.BidRequest, auc *auction) []error { if topBidPerBidder.dealPriority > 0 { if validateAndNormalizeDealTier(impDeal[bidderString]) { - updateHbPbCatDur(topBidPerBidder, impDeal[bidderString].Info) + updateHbPbCatDur(topBidPerBidder, impDeal[bidderString].Info, bidCategory) } else { errs = append(errs, fmt.Errorf("dealTier configuration invalid for bidder '%s', imp ID '%s'", bidderString, impID)) } @@ -258,16 +260,16 @@ func validateAndNormalizeDealTier(impDeal *DealTier) bool { return len(impDeal.Info.Prefix) > 0 && impDeal.Info.MinDealTier > 0 } -func updateHbPbCatDur(bid *pbsOrtbBid, dealTierInfo *DealTierInfo) { +func updateHbPbCatDur(bid *pbsOrtbBid, dealTierInfo *DealTierInfo, bidCategory map[string]string) { if bid.dealPriority >= dealTierInfo.MinDealTier { prefixTier := fmt.Sprintf("%s%d_", dealTierInfo.Prefix, bid.dealPriority) - if oldCatDur, ok := bid.bidTargets["hb_pb_cat_dur"]; ok { + if oldCatDur, ok := bidCategory[bid.bid.ID]; ok { oldCatDurSplit := strings.SplitAfterN(oldCatDur, "_", 2) oldCatDurSplit[0] = prefixTier newCatDur := strings.Join(oldCatDurSplit, "") - bid.bidTargets["hb_pb_cat_dur"] = newCatDur + bidCategory[bid.bid.ID] = newCatDur } } } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 2f115ca4f93..f263eea8569 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1403,7 +1403,10 @@ func TestApplyDealSupport(t *testing.T) { }, } - bid := pbsOrtbBid{&openrtb.Bid{}, "video", test.targ, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + bidCategory := map[string]string{ + bid.bid.ID: test.targ["hb_pb_cat_dur"], + } auc := &auction{ winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ @@ -1413,9 +1416,9 @@ func TestApplyDealSupport(t *testing.T) { }, } - dealErrs := applyDealSupport(bidRequest, auc) + dealErrs := applyDealSupport(bidRequest, auc, bidCategory) - assert.Equal(t, test.expectedHbPbCatDur, auc.winningBidsByBidder["imp_id1"][bidderName].bidTargets["hb_pb_cat_dur"], test.description) + assert.Equal(t, test.expectedHbPbCatDur, bidCategory[auc.winningBidsByBidder["imp_id1"][bidderName].bid.ID], test.description) if len(test.expectedDealErr) > 0 { assert.Containsf(t, dealErrs, errors.New(test.expectedDealErr), "Expected error message not found in deal errors") } @@ -1590,11 +1593,14 @@ func TestUpdateHbPbCatDur(t *testing.T) { } for _, test := range testCases { - bid := pbsOrtbBid{&openrtb.Bid{}, "video", test.targ, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + bid := pbsOrtbBid{&openrtb.Bid{ID: "123456"}, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, test.dealPriority} + bidCategory := map[string]string{ + bid.bid.ID: test.targ["hb_pb_cat_dur"], + } - updateHbPbCatDur(&bid, test.dealTier) + updateHbPbCatDur(&bid, test.dealTier, bidCategory) - assert.Equal(t, test.expectedHbPbCatDur, bid.bidTargets["hb_pb_cat_dur"], test.description) + assert.Equal(t, test.expectedHbPbCatDur, bidCategory[bid.bid.ID], test.description) } } From 733b40d71f38d8b386b3d2bbce02e49476aaa91a Mon Sep 17 00:00:00 2001 From: bretg Date: Mon, 13 Apr 2020 11:21:52 -0400 Subject: [PATCH 047/318] replacing info@prebid.org maintainer email addrs (#1256) --- static/bidder-info/appnexus.yaml | 2 +- static/bidder-info/audienceNetwork.yaml | 2 +- static/bidder-info/ix.yaml | 2 +- static/bidder-info/pulsepoint.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/bidder-info/appnexus.yaml b/static/bidder-info/appnexus.yaml index 585c59b91c6..f1e7ca23cfb 100644 --- a/static/bidder-info/appnexus.yaml +++ b/static/bidder-info/appnexus.yaml @@ -1,5 +1,5 @@ maintainer: - email: "info@prebid.org" + email: "prebid-server@xandr.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/audienceNetwork.yaml b/static/bidder-info/audienceNetwork.yaml index 34700f2f929..56230bf3f9a 100644 --- a/static/bidder-info/audienceNetwork.yaml +++ b/static/bidder-info/audienceNetwork.yaml @@ -1,5 +1,5 @@ maintainer: - email: "info@prebid.org" + email: "none" capabilities: site: mediaTypes: diff --git a/static/bidder-info/ix.yaml b/static/bidder-info/ix.yaml index ff29ec03f77..326989ae9fe 100644 --- a/static/bidder-info/ix.yaml +++ b/static/bidder-info/ix.yaml @@ -1,5 +1,5 @@ maintainer: - email: "info@prebid.org" + email: "pdu-supply-prebid@indexexchange.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/pulsepoint.yaml b/static/bidder-info/pulsepoint.yaml index b9fd32427b1..716e453000e 100644 --- a/static/bidder-info/pulsepoint.yaml +++ b/static/bidder-info/pulsepoint.yaml @@ -1,5 +1,5 @@ maintainer: - email: "info@prebid.org" + email: "ExchangeTeam@pulsepoint.com" capabilities: app: mediaTypes: From 2b334afb690c297f9e72d6d7f466a7fb5fe75518 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 14 Apr 2020 12:26:39 -0400 Subject: [PATCH 048/318] aligning maintainer info (#1258) --- static/bidder-info/33across.yaml | 4 ++-- static/bidder-info/adoppler.yaml | 2 +- static/bidder-info/advangelists.yaml | 2 +- static/bidder-info/brightroll.yaml | 2 +- static/bidder-info/conversant.yaml | 2 +- static/bidder-info/datablocks.yaml | 2 +- static/bidder-info/engagebdr.yaml | 2 +- static/bidder-info/gamoshi.yaml | 2 +- static/bidder-info/gumgum.yaml | 2 +- static/bidder-info/openx.yaml | 2 +- static/bidder-info/rtbhouse.yaml | 2 +- static/bidder-info/sonobi.yaml | 2 +- static/bidder-info/verizonmedia.yaml | 4 ++-- static/bidder-info/visx.yaml | 2 +- static/bidder-info/yieldmo.yaml | 2 +- static/bidder-info/zeroclickfraud.yaml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/static/bidder-info/33across.yaml b/static/bidder-info/33across.yaml index f0a4447099f..84ba6d68611 100644 --- a/static/bidder-info/33across.yaml +++ b/static/bidder-info/33across.yaml @@ -1,9 +1,9 @@ maintainer: - email: "dev@33across.com" + email: "headerbidding@33across.com" capabilities: app: mediaTypes: - banner site: mediaTypes: - - banner \ No newline at end of file + - banner diff --git a/static/bidder-info/adoppler.yaml b/static/bidder-info/adoppler.yaml index 7fa79eda163..1b10103923e 100644 --- a/static/bidder-info/adoppler.yaml +++ b/static/bidder-info/adoppler.yaml @@ -1,5 +1,5 @@ maintainer: - email: info@adoppler.com + email: pbs@adoppler.com capabilities: app: mediaTypes: diff --git a/static/bidder-info/advangelists.yaml b/static/bidder-info/advangelists.yaml index e1bc6c0a19b..aed9900d0e7 100644 --- a/static/bidder-info/advangelists.yaml +++ b/static/bidder-info/advangelists.yaml @@ -1,5 +1,5 @@ maintainer: - email: "lokesh@advangelists.com" + email: "prebid@advangelists.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/brightroll.yaml b/static/bidder-info/brightroll.yaml index 14d9a45f268..f913be6da8c 100644 --- a/static/bidder-info/brightroll.yaml +++ b/static/bidder-info/brightroll.yaml @@ -1,5 +1,5 @@ maintainer: - email: "smithaa@oath.com" + email: "dsp-supply-prebid@verizonmedia.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/conversant.yaml b/static/bidder-info/conversant.yaml index ce67700e380..017f0e0c57e 100644 --- a/static/bidder-info/conversant.yaml +++ b/static/bidder-info/conversant.yaml @@ -1,5 +1,5 @@ maintainer: - email: "mediapsr@conversantmedia.com" + email: "CNVR_PublisherIntegration@conversantmedia.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/datablocks.yaml b/static/bidder-info/datablocks.yaml index 9bf7e780914..43f00a63eae 100644 --- a/static/bidder-info/datablocks.yaml +++ b/static/bidder-info/datablocks.yaml @@ -1,5 +1,5 @@ maintainer: - email: "henry@datablocks.net" + email: "prebid@datablocks.net" capabilities: app: mediaTypes: diff --git a/static/bidder-info/engagebdr.yaml b/static/bidder-info/engagebdr.yaml index d2f7476235f..57c359e451d 100644 --- a/static/bidder-info/engagebdr.yaml +++ b/static/bidder-info/engagebdr.yaml @@ -1,5 +1,5 @@ maintainer: - email: "admin@engagebdr.com" + email: "tech@engagebdr.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/gamoshi.yaml b/static/bidder-info/gamoshi.yaml index 71120ed057e..c3ed3ff10e4 100644 --- a/static/bidder-info/gamoshi.yaml +++ b/static/bidder-info/gamoshi.yaml @@ -1,5 +1,5 @@ maintainer: - email: "moses@gamoshi.com" + email: "dev@gamoshi.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/gumgum.yaml b/static/bidder-info/gumgum.yaml index b8a3981c9f0..0feca7cdf73 100644 --- a/static/bidder-info/gumgum.yaml +++ b/static/bidder-info/gumgum.yaml @@ -1,5 +1,5 @@ maintainer: - email: "pubtech@gumgum.com" + email: "prebid@gumgum.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/openx.yaml b/static/bidder-info/openx.yaml index ce2b67db7da..e3062b54fba 100644 --- a/static/bidder-info/openx.yaml +++ b/static/bidder-info/openx.yaml @@ -1,5 +1,5 @@ maintainer: - email: "team-openx@openx.com" + email: "prebid@openx.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/rtbhouse.yaml b/static/bidder-info/rtbhouse.yaml index 4b899eb3e56..f15af6ca2e1 100644 --- a/static/bidder-info/rtbhouse.yaml +++ b/static/bidder-info/rtbhouse.yaml @@ -1,5 +1,5 @@ maintainer: - email: "inventory.devel@rtbhouse.com" + email: "prebid@rtbhouse.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/sonobi.yaml b/static/bidder-info/sonobi.yaml index f49fa2812b0..6d39319a9f5 100644 --- a/static/bidder-info/sonobi.yaml +++ b/static/bidder-info/sonobi.yaml @@ -1,5 +1,5 @@ maintainer: - email: "apex@sonobi.com" + email: "apex.prebid@sonobi.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/verizonmedia.yaml b/static/bidder-info/verizonmedia.yaml index da5725eec34..024cafadec0 100644 --- a/static/bidder-info/verizonmedia.yaml +++ b/static/bidder-info/verizonmedia.yaml @@ -1,6 +1,6 @@ maintainer: - email: "hb-fe-tech@verizonmedia.com" + email: "dsp-supply-prebid@verizonmedia.com" capabilities: site: mediaTypes: - - banner \ No newline at end of file + - banner diff --git a/static/bidder-info/visx.yaml b/static/bidder-info/visx.yaml index f404a013337..b6a16e4c2d0 100644 --- a/static/bidder-info/visx.yaml +++ b/static/bidder-info/visx.yaml @@ -1,5 +1,5 @@ maintainer: - email: "service@yoc.com" + email: "supply.partners@yoc.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/yieldmo.yaml b/static/bidder-info/yieldmo.yaml index 7d6c0af67cd..514f17455ea 100644 --- a/static/bidder-info/yieldmo.yaml +++ b/static/bidder-info/yieldmo.yaml @@ -1,5 +1,5 @@ maintainer: - email: "progsupport@yieldmo.com" + email: "prebid@yieldmo.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/zeroclickfraud.yaml b/static/bidder-info/zeroclickfraud.yaml index 9bf7e780914..527c0065600 100644 --- a/static/bidder-info/zeroclickfraud.yaml +++ b/static/bidder-info/zeroclickfraud.yaml @@ -1,5 +1,5 @@ maintainer: - email: "henry@datablocks.net" + email: "support@datablocks.net" capabilities: app: mediaTypes: From fb59f73a044e5f15d31e9ae8e9cc81eea62e6fa9 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 14 Apr 2020 12:32:07 -0400 Subject: [PATCH 049/318] Add kidoz bidder info (#1257) got this info from email communication with kidoz --- docs/bidders/kidoz.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/bidders/kidoz.md diff --git a/docs/bidders/kidoz.md b/docs/bidders/kidoz.md new file mode 100644 index 00000000000..433dd71c2ca --- /dev/null +++ b/docs/bidders/kidoz.md @@ -0,0 +1,9 @@ +# Kidoz Bidder + +Kidoz is exclusively for Mobile app COPPA compatible ads, 100% kid relevant and appropriate. + +In order for a company to receive bids from Kidoz, they must first open a publisher account at Kidoz.net +(https://accounts.kidoz.net/publishers/register) and accept the Kidoz Terms and Conditions and Privacy Policy. +Kidoz publishers must confirm that all of their content properties are COPPA and GDPR compliant and perform no monitoring +or tracking of U13 users in their operations. New publishers are provided a Publisher ID and AccessToken, this can also +be used to login to their dashboard at the Kidoz.net portal to monitor their account activity. From c027bac4f780a33d3bd0645949e88d06e6fd3c6e Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Tue, 14 Apr 2020 21:49:38 +0300 Subject: [PATCH 050/318] Add Cropping of BAdv for Rubicon Adapter (#1254) * Add Cropping of BAdv for Rubicon Adapter BAdv size is limited to 50 * Fix after review Co-authored-by: Harbar Dmytro --- adapters/rubicon/rubicon.go | 9 +++++++ adapters/rubicon/rubicon_test.go | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 46caf262108..dad85ee1184 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -21,6 +21,8 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) +const badvLimitSize = 50 + type RubiconAdapter struct { http *adapters.HTTPAdapter URI string @@ -740,6 +742,13 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap request.App = &appCopy } + reqBadv := request.BAdv + if reqBadv != nil { + if len(reqBadv) > badvLimitSize { + request.BAdv = reqBadv[:badvLimitSize] + } + } + request.Imp = []openrtb.Imp{thisImp} request.Cur = nil request.Ext = nil diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index d386daed5b1..96623659d08 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "strconv" "testing" "time" @@ -1133,6 +1134,48 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) { "Unexpected dfp_ad_unit_code: %s", rubiconExtInventory["dfp_ad_unit_code"]) } +func TestOpenRTBRequestWithBadvOverflowed(t *testing.T) { + SIZE_ID := getTestSizes() + bidder := new(RubiconAdapter) + + badvOverflowed := make([]string, 100) + for i := range badvOverflowed { + badvOverflowed[i] = strconv.Itoa(i) + } + + request := &openrtb.BidRequest{ + ID: "test-request-id", + BAdv: badvOverflowed, + Imp: []openrtb.Imp{{ + ID: "test-imp-id", + Banner: &openrtb.Banner{ + Format: []openrtb.Format{ + SIZE_ID[15], + }, + }, + Ext: json.RawMessage(`{ + "bidder": { + "zoneId": 8394, + "siteId": 283282, + "accountId": 7891, + "inventory": {"key1" : "val1"}, + "visitor": {"key2" : "val2"} + } + }`), + }}, + } + + reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{}) + + rubiconReq := &openrtb.BidRequest{} + if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil { + t.Fatalf("Unexpected error while decoding request: %s", err) + } + + badvRequest := rubiconReq.BAdv + assert.Equal(t, badvOverflowed[:50], badvRequest, "Unexpected dfp_ad_unit_code: %s") +} + func TestOpenRTBRequestWithSpecificExtUserEids(t *testing.T) { SIZE_ID := getTestSizes() bidder := new(RubiconAdapter) From d416035355bd7293f54f195a78f386a827b95239 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Wed, 15 Apr 2020 08:33:43 -0700 Subject: [PATCH 051/318] Added metrics support to endpoint aspect (#1226) Co-authored-by: Veronika Solovei --- pbsmetrics/config/metrics.go | 11 ++++ pbsmetrics/config/metrics_test.go | 6 ++ pbsmetrics/go_metrics.go | 18 ++++++ pbsmetrics/go_metrics_test.go | 3 + pbsmetrics/metrics.go | 13 ++-- pbsmetrics/metrics_mock.go | 5 ++ pbsmetrics/prometheus/preload.go | 8 +++ pbsmetrics/prometheus/prometheus.go | 24 ++++++++ pbsmetrics/prometheus/prometheus_test.go | 60 +++++++++++++++++++ router/aspects/request_timeout_handler.go | 8 ++- .../aspects/request_timeout_handler_test.go | 44 ++++++-------- router/router.go | 3 +- 12 files changed, 170 insertions(+), 33 deletions(-) diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go index 81cfbfd0798..e1cdaceb0e5 100644 --- a/pbsmetrics/config/metrics.go +++ b/pbsmetrics/config/metrics.go @@ -181,6 +181,13 @@ func (me *MultiMetricsEngine) RecordPrebidCacheRequestTime(success bool, length } } +// RecordRequestQueueTime across all engines +func (me *MultiMetricsEngine) RecordRequestQueueTime(success bool, requestType pbsmetrics.RequestType, length time.Duration) { + for _, thisME := range *me { + thisME.RecordRequestQueueTime(success, requestType, length) + } +} + // DummyMetricsEngine is a Noop metrics engine in case no metrics are configured. (may also be useful for tests) type DummyMetricsEngine struct{} @@ -251,3 +258,7 @@ func (me *DummyMetricsEngine) RecordStoredImpCacheResult(cacheResult pbsmetrics. // RecordPrebidCacheRequestTime as a noop func (me *DummyMetricsEngine) RecordPrebidCacheRequestTime(success bool, length time.Duration) { } + +// RecordRequestQueueTime as a noop +func (me *DummyMetricsEngine) RecordRequestQueueTime(success bool, requestType pbsmetrics.RequestType, length time.Duration) { +} diff --git a/pbsmetrics/config/metrics_test.go b/pbsmetrics/config/metrics_test.go index ad817ba75a9..d2374f95195 100644 --- a/pbsmetrics/config/metrics_test.go +++ b/pbsmetrics/config/metrics_test.go @@ -115,6 +115,9 @@ func TestMultiMetricsEngine(t *testing.T) { for i := 0; i < 3; i++ { metricsEngine.RecordImps(impTypeLabels) } + + metricsEngine.RecordRequestQueueTime(false, pbsmetrics.ReqTypeVideo, time.Duration(1)) + //Make the metrics engine, instantiated here with goEngine, fill its RequestStatuses[RequestType][pbsmetrics.RequestStatusXX] with the new boolean values added to pbsmetrics.Labels VerifyMetrics(t, "RequestStatuses.OpenRTB2.OK", goEngine.RequestStatuses[pbsmetrics.ReqTypeORTB2Web][pbsmetrics.RequestStatusOK].Count(), 5) VerifyMetrics(t, "RequestStatuses.Legacy.OK", goEngine.RequestStatuses[pbsmetrics.ReqTypeLegacy][pbsmetrics.RequestStatusOK].Count(), 0) @@ -148,6 +151,9 @@ func TestMultiMetricsEngine(t *testing.T) { } VerifyMetrics(t, "AdapterMetrics.AppNexus.GotBidsMeter", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].GotBidsMeter.Count(), 0) VerifyMetrics(t, "AdapterMetrics.AppNexus.NoBidMeter", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].NoBidMeter.Count(), 5) + + VerifyMetrics(t, "RecordRequestQueueTime.Video.Rejected", goEngine.RequestsQueueTimer[pbsmetrics.ReqTypeVideo][false].Count(), 1) + VerifyMetrics(t, "RecordRequestQueueTime.Video.Accepted", goEngine.RequestsQueueTimer[pbsmetrics.ReqTypeVideo][true].Count(), 0) } func VerifyMetrics(t *testing.T, name string, actual int64, expected int64) { diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go index 9b3dd65ff4e..ff3d9681fb1 100644 --- a/pbsmetrics/go_metrics.go +++ b/pbsmetrics/go_metrics.go @@ -24,6 +24,7 @@ type Metrics struct { SafariRequestMeter metrics.Meter SafariNoCookieMeter metrics.Meter RequestTimer metrics.Timer + RequestsQueueTimer map[RequestType]map[bool]metrics.Timer PrebidCacheRequestTimerSuccess metrics.Timer PrebidCacheRequestTimerError metrics.Timer StoredReqCacheMeter map[CacheResult]metrics.Meter @@ -111,6 +112,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa SafariRequestMeter: blankMeter, SafariNoCookieMeter: blankMeter, RequestTimer: blankTimer, + RequestsQueueTimer: make(map[RequestType]map[bool]metrics.Timer), PrebidCacheRequestTimerSuccess: blankTimer, PrebidCacheRequestTimerError: blankTimer, StoredReqCacheMeter: make(map[CacheResult]metrics.Meter), @@ -146,6 +148,11 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa } } + //to minimize memory usage, queuedTimeout metric is now supported for video endpoint only + //boolean value represents 2 general request statuses: accepted and rejected + newMetrics.RequestsQueueTimer["video"] = make(map[bool]metrics.Timer) + newMetrics.RequestsQueueTimer["video"][true] = blankTimer + newMetrics.RequestsQueueTimer["video"][false] = blankTimer return newMetrics } @@ -191,11 +198,15 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d statusMap[stat] = metrics.GetOrRegisterMeter("requests."+string(stat)+"."+string(typ), registry) } } + for _, cacheRes := range CacheResults() { newMetrics.StoredReqCacheMeter[cacheRes] = metrics.GetOrRegisterMeter(fmt.Sprintf("stored_request_cache_%s", string(cacheRes)), registry) newMetrics.StoredImpCacheMeter[cacheRes] = metrics.GetOrRegisterMeter(fmt.Sprintf("stored_imp_cache_%s", string(cacheRes)), registry) } + newMetrics.RequestsQueueTimer["video"][true] = metrics.GetOrRegisterTimer("queued_requests.video.accepted", registry) + newMetrics.RequestsQueueTimer["video"][false] = metrics.GetOrRegisterTimer("queued_requests.video.rejected", registry) + newMetrics.userSyncSet[unknownBidder] = metrics.GetOrRegisterMeter("usersync.unknown.sets", registry) newMetrics.userSyncGDPRPrevent[unknownBidder] = metrics.GetOrRegisterMeter("usersync.unknown.gdpr_prevent", registry) return newMetrics @@ -526,6 +537,13 @@ func (me *Metrics) RecordPrebidCacheRequestTime(success bool, length time.Durati } } +func (me *Metrics) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) { + if requestType == ReqTypeVideo { //remove this check when other request types are supported + me.RequestsQueueTimer[requestType][success].Update(length) + } + +} + func doMark(bidder openrtb_ext.BidderName, meters map[openrtb_ext.BidderName]metrics.Meter) { met, ok := meters[bidder] if ok { diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go index b403733dcc7..253ff69e3c2 100644 --- a/pbsmetrics/go_metrics_test.go +++ b/pbsmetrics/go_metrics_test.go @@ -50,6 +50,9 @@ func TestNewMetrics(t *testing.T) { ensureContains(t, registry, "requests.badinput.video", m.RequestStatuses[ReqTypeVideo][RequestStatusBadInput]) ensureContains(t, registry, "requests.err.video", m.RequestStatuses[ReqTypeVideo][RequestStatusErr]) ensureContains(t, registry, "requests.networkerr.video", m.RequestStatuses[ReqTypeVideo][RequestStatusNetworkErr]) + + ensureContains(t, registry, "queued_requests.video.rejected", m.RequestsQueueTimer[ReqTypeVideo][false]) + ensureContains(t, registry, "queued_requests.video.accepted", m.RequestsQueueTimer[ReqTypeVideo][true]) } func TestRecordBidType(t *testing.T) { diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index cc836011efa..611692c9c01 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -154,11 +154,12 @@ func CookieTypes() []CookieFlag { // Request/return status const ( - RequestStatusOK RequestStatus = "ok" - RequestStatusBadInput RequestStatus = "badinput" - RequestStatusErr RequestStatus = "err" - RequestStatusNetworkErr RequestStatus = "networkerr" - RequestStatusBlacklisted RequestStatus = "blacklistedacctorapp" + RequestStatusOK RequestStatus = "ok" + RequestStatusBadInput RequestStatus = "badinput" + RequestStatusErr RequestStatus = "err" + RequestStatusNetworkErr RequestStatus = "networkerr" + RequestStatusBlacklisted RequestStatus = "blacklistedacctorapp" + RequestStatusQueueTimeout RequestStatus = "queuetimeout" ) func RequestStatuses() []RequestStatus { @@ -168,6 +169,7 @@ func RequestStatuses() []RequestStatus { RequestStatusErr, RequestStatusNetworkErr, RequestStatusBlacklisted, + RequestStatusQueueTimeout, } } @@ -272,4 +274,5 @@ type MetricsEngine interface { RecordStoredReqCacheResult(cacheResult CacheResult, inc int) RecordStoredImpCacheResult(cacheResult CacheResult, inc int) RecordPrebidCacheRequestTime(success bool, length time.Duration) + RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) } diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go index 6d57f9fcfaa..1f5b84b1e0f 100644 --- a/pbsmetrics/metrics_mock.go +++ b/pbsmetrics/metrics_mock.go @@ -96,3 +96,8 @@ func (me *MetricsEngineMock) RecordStoredImpCacheResult(cacheResult CacheResult, func (me *MetricsEngineMock) RecordPrebidCacheRequestTime(success bool, length time.Duration) { me.Called(success, length) } + +// RecordRequestQueueTime mock +func (me *MetricsEngineMock) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) { + me.Called(success, requestType, length) +} diff --git a/pbsmetrics/prometheus/preload.go b/pbsmetrics/prometheus/preload.go index 7654dd54f82..11e6bdc14d8 100644 --- a/pbsmetrics/prometheus/preload.go +++ b/pbsmetrics/prometheus/preload.go @@ -1,6 +1,7 @@ package prometheusmetrics import ( + "github.com/prebid/prebid-server/pbsmetrics" "github.com/prometheus/client_golang/prometheus" ) @@ -91,6 +92,13 @@ func preloadLabelValues(m *Metrics) { adapterLabel: adapterValues, actionLabel: actionValues, }) + + //to minimize memory usage, queuedTimeout metric is now supported for video endpoint only + //boolean value represents 2 general request statuses: accepted and rejected + preloadLabelValuesForHistogram(m.requestsQueueTimer, map[string][]string{ + requestTypeLabel: {string(pbsmetrics.ReqTypeVideo)}, + requestStatusLabel: {requestSuccessLabel, requestRejectLabel}, + }) } func preloadLabelValuesForCounter(counter *prometheus.CounterVec, labelsWithValues map[string][]string) { diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index e2b646d5238..d66defea4cd 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -24,6 +24,7 @@ type Metrics struct { prebidCacheWriteTimer *prometheus.HistogramVec requests *prometheus.CounterVec requestsTimer *prometheus.HistogramVec + requestsQueueTimer *prometheus.HistogramVec requestsWithoutCookie *prometheus.CounterVec storedImpressionsCacheResult *prometheus.CounterVec storedRequestCacheResult *prometheus.CounterVec @@ -73,11 +74,17 @@ const ( markupDeliveryNurl = "nurl" ) +const ( + requestSuccessLabel = "requestAcceptedLabel" + requestRejectLabel = "requestRejectedLabel" +) + // NewMetrics initializes a new Prometheus metrics instance with preloaded label values. func NewMetrics(cfg config.PrometheusMetrics) *Metrics { requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} cacheWriteTimeBuckets := []float64{0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1} priceBuckets := []float64{250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000} + queuedRequestTimeBuckets := []float64{0, 1, 5, 30, 60, 120, 180, 240, 300} metrics := Metrics{} metrics.Registry = prometheus.NewRegistry() @@ -187,6 +194,12 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of total requests to Prebid Server labeled by account.", []string{accountLabel}) + metrics.requestsQueueTimer = newHistogram(cfg, metrics.Registry, + "request_queue_time", + "Seconds request was waiting in queue", + []string{requestTypeLabel, requestStatusLabel}, + queuedRequestTimeBuckets) + preloadLabelValues(&metrics) return &metrics @@ -374,3 +387,14 @@ func (m *Metrics) RecordPrebidCacheRequestTime(success bool, length time.Duratio successLabel: strconv.FormatBool(success), }).Observe(length.Seconds()) } + +func (m *Metrics) RecordRequestQueueTime(success bool, requestType pbsmetrics.RequestType, length time.Duration) { + successLabelFormatted := requestRejectLabel + if success { + successLabelFormatted = requestSuccessLabel + } + m.requestsQueueTimer.With(prometheus.Labels{ + requestTypeLabel: string(requestType), + requestStatusLabel: successLabelFormatted, + }).Observe(length.Seconds()) +} diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index f76480f0852..e4d6a4f78d1 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -881,6 +881,48 @@ func TestMetricAccumulationSpotCheck(t *testing.T) { expectedValue) } +func TestRecordRequestQueueTimeMetric(t *testing.T) { + performTest := func(m *Metrics, requestStatus bool, requestType pbsmetrics.RequestType, timeInSec float64) { + m.RecordRequestQueueTime(requestStatus, requestType, time.Duration(timeInSec*float64(time.Second))) + } + + testCases := []struct { + description string + status string + testCase func(m *Metrics) + expectedCount uint64 + expectedSum float64 + }{ + { + description: "Success", + status: requestSuccessLabel, + testCase: func(m *Metrics) { + performTest(m, true, pbsmetrics.ReqTypeVideo, 2) + }, + expectedCount: 1, + expectedSum: 2, + }, + { + description: "TimeoutError", + status: requestRejectLabel, + testCase: func(m *Metrics) { + performTest(m, false, pbsmetrics.ReqTypeVideo, 50) + }, + expectedCount: 1, + expectedSum: 50, + }, + } + + m := createMetricsForTesting() + for _, test := range testCases { + + test.testCase(m) + + result := getHistogramFromHistogramVecByTwoKeys(m.requestsQueueTimer, requestTypeLabel, "video", requestStatusLabel, test.status) + assertHistogram(t, test.description, result, test.expectedCount, test.expectedSum) + } +} + func assertCounterValue(t *testing.T, description, name string, counter prometheus.Counter, expected float64) { m := dto.Metric{} counter.Write(&m) @@ -906,6 +948,24 @@ func getHistogramFromHistogramVec(histogram *prometheus.HistogramVec, labelKey, return result } +func getHistogramFromHistogramVecByTwoKeys(histogram *prometheus.HistogramVec, label1Key, label1Value, label2Key, label2Value string) dto.Histogram { + var result dto.Histogram + processMetrics(histogram, func(m dto.Metric) { + for ind, label := range m.GetLabel() { + if label.GetName() == label1Key && label.GetValue() == label1Value { + valInd := ind + if ind == 1 { + valInd = 0 + } + if m.Label[valInd].GetName() == label2Key && m.Label[valInd].GetValue() == label2Value { + result = *m.GetHistogram() + } + } + } + }) + return result +} + func processMetrics(collector prometheus.Collector, handler func(m dto.Metric)) { collectorChan := make(chan prometheus.Metric) go func() { diff --git a/router/aspects/request_timeout_handler.go b/router/aspects/request_timeout_handler.go index ae11f8c5614..23d6cef9faf 100644 --- a/router/aspects/request_timeout_handler.go +++ b/router/aspects/request_timeout_handler.go @@ -3,11 +3,13 @@ package aspects import ( "github.com/julienschmidt/httprouter" "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/pbsmetrics" "net/http" "strconv" + "time" ) -func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders) httprouter.Handle { +func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders, metricsEngine pbsmetrics.MetricsEngine, requestType pbsmetrics.RequestType) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { @@ -30,13 +32,17 @@ func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestT return } + reqTimeDuration := time.Duration(reqTimeFloat * float64(time.Second)) + //Return HTTP 408 if requests stays too long in queue if reqTimeFloat >= reqTimeoutFloat { w.WriteHeader(http.StatusRequestTimeout) w.Write([]byte("Queued request processing time exceeded maximum")) + metricsEngine.RecordRequestQueueTime(false, requestType, reqTimeDuration) return } + metricsEngine.RecordRequestQueueTime(true, requestType, reqTimeDuration) f(w, r, params) } diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go index 5283d5d51e7..cdc920c4263 100644 --- a/router/aspects/request_timeout_handler_test.go +++ b/router/aspects/request_timeout_handler_test.go @@ -1,12 +1,14 @@ package aspects import ( + "github.com/julienschmidt/httprouter" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/pbsmetrics" "net/http" "net/http/httptest" + "strconv" "testing" - - "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" + "time" "github.com/stretchr/testify/assert" ) @@ -23,6 +25,7 @@ func TestAny(t *testing.T) { expectedRespCodeMessage string expectedRespBody string expectedRespBodyMessage string + requestStatusMetrics bool }{ { //TestQueuedRequestTimeoutWithTimeout @@ -33,6 +36,7 @@ func TestAny(t *testing.T) { expectedRespCodeMessage: "Http response code is incorrect, should be 408", expectedRespBody: "Queued request processing time exceeded maximum", expectedRespBodyMessage: "Body should have error message", + requestStatusMetrics: false, }, { //TestQueuedRequestTimeoutNoTimeout @@ -43,6 +47,7 @@ func TestAny(t *testing.T) { expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", + requestStatusMetrics: true, }, { //TestQueuedRequestNoHeaders @@ -53,6 +58,7 @@ func TestAny(t *testing.T) { expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", + requestStatusMetrics: true, }, { //TestQueuedRequestSomeHeaders @@ -63,31 +69,13 @@ func TestAny(t *testing.T) { expectedRespCodeMessage: "Http response code is incorrect, should be 200", expectedRespBody: "Executed", expectedRespBodyMessage: "Body should be present in response", - }, - { - //TestQueuedRequestAllHeadersIncorrect - reqTimeInQueue: "test1", - reqTimeOut: "test2", - setHeaders: true, - expectedRespCode: http.StatusInternalServerError, - expectedRespCodeMessage: "Http response code is incorrect, should be 400", - expectedRespBody: "Request timeout headers are incorrect (wrong format)", - expectedRespBodyMessage: "Body should have error message", - }, - { - //TestQueuedRequestSomeHeadersIncorrect - reqTimeInQueue: "test1", - reqTimeOut: "123", - setHeaders: true, - expectedRespCode: http.StatusInternalServerError, - expectedRespCodeMessage: "Http response code is incorrect, should be 400", - expectedRespBody: "Request timeout headers are incorrect (wrong format)", - expectedRespBodyMessage: "Body should have error message", + requestStatusMetrics: true, }, } for _, test := range testCases { - result := ExecuteAspectRequest(t, test.reqTimeInQueue, test.reqTimeOut, test.setHeaders) + reqTimeFloat, _ := strconv.ParseFloat(test.reqTimeInQueue, 64) + result := ExecuteAspectRequest(t, test.reqTimeInQueue, test.reqTimeOut, test.setHeaders, pbsmetrics.ReqTypeVideo, test.requestStatusMetrics, reqTimeFloat) assert.Equal(t, test.expectedRespCode, result.Code, test.expectedRespCodeMessage) assert.Equal(t, test.expectedRespBody, string(result.Body.Bytes()), test.expectedRespBodyMessage) } @@ -101,7 +89,7 @@ func MockHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { w.Write([]byte("Executed")) } -func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, setHeaders bool) *httptest.ResponseRecorder { +func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, setHeaders bool, requestType pbsmetrics.RequestType, status bool, requestDuration float64) *httptest.ResponseRecorder { rw := httptest.NewRecorder() req, err := http.NewRequest("POST", "/test", nil) if err != nil { @@ -114,7 +102,11 @@ func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, s customHeaders := config.RequestTimeoutHeaders{reqTimeInQueueHeaderName, reqTimeoutHeaderName} - handler := QueuedRequestTimeout(MockEndpoint(), customHeaders) + metrics := &pbsmetrics.MetricsEngineMock{} + + metrics.On("RecordRequestQueueTime", status, requestType, time.Duration(requestDuration*float64(time.Second))).Once() + + handler := QueuedRequestTimeout(MockEndpoint(), customHeaders, metrics, requestType) r := httprouter.New() r.POST("/test", handler) diff --git a/router/router.go b/router/router.go index 8ac463b85a0..68627f937dd 100644 --- a/router/router.go +++ b/router/router.go @@ -6,6 +6,7 @@ import ( "database/sql" "encoding/json" "fmt" + "github.com/prebid/prebid-server/pbsmetrics" "io/ioutil" "net/http" "path/filepath" @@ -258,7 +259,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r requestTimeoutHeaders := config.RequestTimeoutHeaders{} if cfg.RequestTimeoutHeaders != requestTimeoutHeaders { - videoEndpoint = aspects.QueuedRequestTimeout(videoEndpoint, cfg.RequestTimeoutHeaders) + videoEndpoint = aspects.QueuedRequestTimeout(videoEndpoint, cfg.RequestTimeoutHeaders, r.MetricsEngine, pbsmetrics.ReqTypeVideo) } r.POST("/auction", endpoints.Auction(cfg, syncers, gdprPerms, r.MetricsEngine, dataCache, exchanges)) From cc7a247bee26b46c53d6bf7473b9146ce8f8ef57 Mon Sep 17 00:00:00 2001 From: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Date: Wed, 15 Apr 2020 10:21:16 -0700 Subject: [PATCH 052/318] Prebid Server adapter for Telaria (#1231) * TELARIA adapter. First Pass * Some refactoring * added the json files * fixed some tests and added the bidder info * fixed some tests and added the bidder info * added default user sync ur; * - Handling gzipped responses from our server * - more refactoring. * added the proper user sync default URL * changed the urls from dev to prod * changed up the required fields. Now AdCode in the Imp.Ext isn't required but Bid.SeatCode is required * change in the return type after decompressing * some refactoring * change in our config url * using pbs.yml to switch between our production and test URLs * setting default endpoint * - fixed the issue that was preventing telaria test cases to run. - added more test cases * - Modifications as per the changes requested by the maintainers. * Moved the seat code to imp.ext * Moved the seat code to imp.ext * Added 'Telaria: ' prefix for error messages * - Fixes for race conditions. Was modifying the original request object instead of a copy * cosmetic changes. * added params_test.go Co-authored-by: Vinay Prasad --- adapters/telaria/params_test.go | 50 +++ adapters/telaria/telaria.go | 330 ++++++++++++++++++ adapters/telaria/telaria_test.go | 34 ++ .../telariatest/exemplary/video-app.json | 157 +++++++++ .../telariatest/exemplary/video-web.json | 145 ++++++++ .../telariatest/params/race/video.json | 4 + .../supplemental/banner-unsupported.json | 42 +++ .../supplemental/invalid-response.json | 105 ++++++ .../invalid-telaria-ext-object.json | 29 ++ .../supplemental/requires-imp-object.json | 16 + .../supplemental/requires-seat-code.json | 30 ++ .../supplemental/requires-video-object.json | 26 ++ .../supplemental/status-code-bad-request.json | 80 +++++ .../supplemental/status-code-no-content.json | 83 +++++ .../supplemental/status-code-other-error.json | 83 +++++ .../status-code-service-unavailable.json | 83 +++++ adapters/telaria/usersync.go | 12 + adapters/telaria/usersync_test.go | 33 ++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_telaria.go | 6 + static/bidder-info/telaria.yaml | 9 + static/bidder-params/telaria.json | 22 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 26 files changed, 1388 insertions(+) create mode 100644 adapters/telaria/params_test.go create mode 100644 adapters/telaria/telaria.go create mode 100644 adapters/telaria/telaria_test.go create mode 100644 adapters/telaria/telariatest/exemplary/video-app.json create mode 100644 adapters/telaria/telariatest/exemplary/video-web.json create mode 100644 adapters/telaria/telariatest/params/race/video.json create mode 100644 adapters/telaria/telariatest/supplemental/banner-unsupported.json create mode 100644 adapters/telaria/telariatest/supplemental/invalid-response.json create mode 100644 adapters/telaria/telariatest/supplemental/invalid-telaria-ext-object.json create mode 100644 adapters/telaria/telariatest/supplemental/requires-imp-object.json create mode 100644 adapters/telaria/telariatest/supplemental/requires-seat-code.json create mode 100644 adapters/telaria/telariatest/supplemental/requires-video-object.json create mode 100644 adapters/telaria/telariatest/supplemental/status-code-bad-request.json create mode 100644 adapters/telaria/telariatest/supplemental/status-code-no-content.json create mode 100644 adapters/telaria/telariatest/supplemental/status-code-other-error.json create mode 100644 adapters/telaria/telariatest/supplemental/status-code-service-unavailable.json create mode 100644 adapters/telaria/usersync.go create mode 100644 adapters/telaria/usersync_test.go create mode 100644 openrtb_ext/imp_telaria.go create mode 100644 static/bidder-info/telaria.yaml create mode 100644 static/bidder-params/telaria.json diff --git a/adapters/telaria/params_test.go b/adapters/telaria/params_test.go new file mode 100644 index 00000000000..efa3fba1be9 --- /dev/null +++ b/adapters/telaria/params_test.go @@ -0,0 +1,50 @@ +package telaria + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderTelaria, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Telaria params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the Telaria schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderTelaria, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"adCode": "string", "seatCode": "string", "originalPublisherid": "string"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"adCode": "string", "originalPublisherid": "string"}`, + `{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`, +} diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go new file mode 100644 index 00000000000..9edafa86a32 --- /dev/null +++ b/adapters/telaria/telaria.go @@ -0,0 +1,330 @@ +package telaria + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "strconv" +) + +const Endpoint = "https://ads.tremorhub.com/ad/rtb/prebid" + +type TelariaAdapter struct { + URI string +} + +// This will be part of Imp[i].Ext when this adapter calls out the Telaria Ad Server +type ImpressionExtOut struct { + OriginalTagID string `json:"originalTagid"` + OriginalPublisherID string `json:"originalPublisherid"` +} + +// used for cookies and such +func (a *TelariaAdapter) Name() string { + return "telaria" +} + +func (a *TelariaAdapter) SkipNoCookies() bool { + return false +} + +// Endpoint for Telaria Ad server +func (a *TelariaAdapter) FetchEndpoint() string { + return a.URI +} + +// Checker method to ensure len(request.Imp) > 0 +func (a *TelariaAdapter) CheckHasImps(request *openrtb.BidRequest) error { + if len(request.Imp) == 0 { + err := &errortypes.BadInput{ + Message: "Telaria: Missing Imp Object", + } + return err + } + return nil +} + +// Checking if Imp[i].Video exists and Imp[i].Banner doesn't exist +func (a *TelariaAdapter) CheckHasVideoObject(request *openrtb.BidRequest) error { + hasVideoObject := false + + for _, imp := range request.Imp { + if imp.Banner != nil { + return &errortypes.BadInput{ + Message: "Telaria: Banner not supported", + } + } + + hasVideoObject = hasVideoObject || imp.Video != nil + } + + if !hasVideoObject { + return &errortypes.BadInput{ + Message: "Telaria: Only Supports Video", + } + } + + return nil +} + +// Fetches the populated header object +func GetHeaders(request *openrtb.BidRequest) *http.Header { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("X-Openrtb-Version", "2.5") + headers.Add("Accept-Encoding", "gzip") + + if request.Device != nil { + if len(request.Device.UA) > 0 { + headers.Add("User-Agent", request.Device.UA) + } + + if len(request.Device.IP) > 0 { + headers.Add("X-Forwarded-For", request.Device.IP) + } + + if len(request.Device.Language) > 0 { + headers.Add("Accept-Language", request.Device.Language) + } + + if request.Device.DNT != nil { + headers.Add("Dnt", strconv.Itoa(int(*request.Device.DNT))) + } + } + + return &headers +} + +// Checks the imp[i].ext object and returns a imp.ext object as per ExtImpTelaria format +func (a *TelariaAdapter) FetchTelariaExtImpParams(imp *openrtb.Imp) (*openrtb_ext.ExtImpTelaria, error) { + var bidderExt adapters.ExtImpBidder + err := json.Unmarshal(imp.Ext, &bidderExt) + + if err != nil { + err = &errortypes.BadInput{ + Message: "Telaria: ext.bidder not provided", + } + + return nil, err + } + + var telariaExt openrtb_ext.ExtImpTelaria + err = json.Unmarshal(bidderExt.Bidder, &telariaExt) + + if err != nil { + return nil, err + } + + if telariaExt.SeatCode == "" { + return nil, &errortypes.BadInput{Message: "Telaria: Seat Code required"} + } + + return &telariaExt, nil +} + +// Method to fetch the original publisher ID. Note that this method must be called +// before we replace publisher.ID with seatCode +func (a *TelariaAdapter) FetchOriginalPublisherID(request *openrtb.BidRequest) string { + + if request.Site != nil && request.Site.Publisher != nil { + return request.Site.Publisher.ID + } else if request.App != nil && request.App.Publisher != nil { + return request.App.Publisher.ID + } + + return "" +} + +// Method to do a deep copy of the publisher object. It also adds the seatCode as publisher.ID +func (a *TelariaAdapter) MakePublisherObject(seatCode string, publisher *openrtb.Publisher) *openrtb.Publisher { + var pub = &openrtb.Publisher{ID: seatCode} + + if publisher != nil { + pub.Domain = publisher.Domain + pub.Name = publisher.Name + pub.Cat = publisher.Cat + pub.Ext = publisher.Ext + } + + return pub +} + +// This method changes .publisher.id to the seatCode +func (a *TelariaAdapter) PopulatePublisherId(request *openrtb.BidRequest, seatCode string) (*openrtb.Site, *openrtb.App) { + if request.Site != nil { + siteCopy := *request.Site + siteCopy.Publisher = a.MakePublisherObject(seatCode, request.Site.Publisher) + return &siteCopy, nil + } else if request.App != nil { + appCopy := *request.App + appCopy.Publisher = a.MakePublisherObject(seatCode, request.App.Publisher) + return nil, &appCopy + } + return nil, nil +} + +func (a *TelariaAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + // make a copy of the incoming request + request := *requestIn + + // ensure that the request has Impressions + if noImps := a.CheckHasImps(&request); noImps != nil { + return nil, []error{noImps} + } + + // ensure that the request has a Video object + if noVideoObjectError := a.CheckHasVideoObject(&request); noVideoObjectError != nil { + return nil, []error{noVideoObjectError} + } + + var seatCode string + originalPublisherID := a.FetchOriginalPublisherID(&request) + + var errors []error + for i, imp := range request.Imp { + // fetch adCode & seatCode from Imp[i].Ext + telariaExt, err := a.FetchTelariaExtImpParams(&imp) + if err != nil { + errors = append(errors, err) + break + } + + seatCode = telariaExt.SeatCode + + // move the original tagId and the original publisher.id into the Imp[i].Ext object + request.Imp[i].Ext, err = json.Marshal(&ImpressionExtOut{request.Imp[i].TagID, originalPublisherID}) + if err != nil { + errors = append(errors, err) + break + } + + // Swap the tagID with adCode + request.Imp[i].TagID = telariaExt.AdCode + } + + if len(errors) > 0 { + return nil, errors + } + + // Add seatCode to .Publisher.ID + siteObject, appObject := a.PopulatePublisherId(&request, seatCode) + + request.Site = siteObject + request.App = appObject + + reqJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.FetchEndpoint(), + Body: reqJSON, + Headers: *GetHeaders(&request), + }}, nil +} + +// response isn't automatically decompressed. This method unzips the response if Content-Encoding is gzip +func GetResponseBody(response *adapters.ResponseData) ([]byte, error) { + + if "gzip" == response.Headers.Get("Content-Encoding") { + body := bytes.NewBuffer(response.Body) + r, readerErr := gzip.NewReader(body) + if readerErr != nil { + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Error while trying to unzip data [ %d ]", response.StatusCode), + } + } + var resB bytes.Buffer + var err error + _, err = resB.ReadFrom(r) + if err != nil { + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Error while trying to unzip data [ %d ]", response.StatusCode), + } + } + + response.Headers.Del("Content-Encoding") + + return resB.Bytes(), nil + } else { + return response.Body, nil + } +} + +func (a *TelariaAdapter) CheckResponseStatusCodes(response *adapters.ResponseData) error { + if response.StatusCode == http.StatusNoContent { + return &errortypes.BadInput{Message: "Telaria: Invalid Bid Request received by the server"} + } + + if response.StatusCode == http.StatusBadRequest { + return &errortypes.BadInput{ + Message: fmt.Sprintf("Telaria: Unexpected status code: [ %d ] ", response.StatusCode), + } + } + + if response.StatusCode == http.StatusServiceUnavailable { + return &errortypes.BadInput{ + Message: fmt.Sprintf("Telaria: Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), + } + } + + if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { + return &errortypes.BadInput{ + Message: fmt.Sprintf("Telaria: Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), + } + } + + return nil +} + +func (a *TelariaAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + httpStatusError := a.CheckResponseStatusCodes(response) + if httpStatusError != nil { + return nil, []error{httpStatusError} + } + + responseBody, err := GetResponseBody(response) + + if err != nil { + return nil, []error{err} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(responseBody, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Telaria: Bad Server Response", + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + sb := bidResp.SeatBid[0] + + for _, bid := range sb.Bid { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: openrtb_ext.BidTypeVideo, + }) + } + return bidResponse, nil +} + +func NewTelariaBidder(endpoint string) *TelariaAdapter { + if endpoint == "" { + endpoint = Endpoint + } + + return &TelariaAdapter{ + URI: endpoint, + } +} diff --git a/adapters/telaria/telaria_test.go b/adapters/telaria/telaria_test.go new file mode 100644 index 00000000000..7ad96b9307b --- /dev/null +++ b/adapters/telaria/telaria_test.go @@ -0,0 +1,34 @@ +package telaria + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +/** + * Verify adapter names are setup correctly. + */ +func TestTelariaAdapterNames(t *testing.T) { + adapter := NewTelariaBidder("") + adapterstest.VerifyStringValue(adapter.Name(), "telaria", t) +} + +/** + * Verify adapter SkipNoCookie is correct. + */ +func TestTelariaAdapterSkipNoCookiesFlag(t *testing.T) { + adapter := NewTelariaBidder("") + adapterstest.VerifyBoolValue(adapter.SkipNoCookies(), false, t) +} + +/** + * Verify bidder has the proper URL + */ +func TestTelariaAdapterEndpoint(t *testing.T) { + adapter := NewTelariaBidder("") + adapterstest.VerifyStringValue(adapter.URI, "https://ads.tremorhub.com/ad/rtb/prebid", t) +} + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "telariatest", NewTelariaBidder("")) +} diff --git a/adapters/telaria/telariatest/exemplary/video-app.json b/adapters/telaria/telariatest/exemplary/video-app.json new file mode 100644 index 00000000000..09bcf998454 --- /dev/null +++ b/adapters/telaria/telariatest/exemplary/video-app.json @@ -0,0 +1,157 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"], + "X-Openrtb-Version": ["2.5"], + "Accept-Encoding": ["gzip"], + "User-Agent": ["test-user-agent"], + "X-Forwarded-For": ["123.123.123.123"], + "Accept-Language": ["en"], + "Dnt": ["0"] + }, + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "my-adcode", + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [{ + "bid": [{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + }], + "seat": "telaria" + }], + "cur": "USD", + "ext": { + "responsetimemillis": { + "telaria": 154 + }, + "tmaxrequest": 1000 + } + } + } + }], + "expectedBids": [{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + }] +} diff --git a/adapters/telaria/telariatest/exemplary/video-web.json b/adapters/telaria/telariatest/exemplary/video-web.json new file mode 100644 index 00000000000..5dc26b0f018 --- /dev/null +++ b/adapters/telaria/telariatest/exemplary/video-web.json @@ -0,0 +1,145 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"], + "X-Openrtb-Version": ["2.5"], + "Accept-Encoding": ["gzip"], + "User-Agent": ["test-user-agent"], + "X-Forwarded-For": ["123.123.123.123"], + "Accept-Language": ["en"], + "Dnt": ["0"] + }, + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [{ + "bid": [{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + }], + "seat": "telaria" + }], + "cur": "USD", + "ext": { + "responsetimemillis": { + "telaria": 154 + }, + "tmaxrequest": 1000 + } + } + } + }], + "expectedBids": [{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + }] +} diff --git a/adapters/telaria/telariatest/params/race/video.json b/adapters/telaria/telariatest/params/race/video.json new file mode 100644 index 00000000000..e3b67ec8c20 --- /dev/null +++ b/adapters/telaria/telariatest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "adCode": "my-adcode", + "seatCode": "my-seatcode" +} diff --git a/adapters/telaria/telariatest/supplemental/banner-unsupported.json b/adapters/telaria/telariatest/supplemental/banner-unsupported.json new file mode 100644 index 00000000000..1e75371dd22 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/banner-unsupported.json @@ -0,0 +1,42 @@ + +{ + "expectedMakeRequestsErrors": [ + { + "value": "Telaria: Banner not supported", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "test.com", + "publisher": { + "id": "someother-publisher-id" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + } +} diff --git a/adapters/telaria/telariatest/supplemental/invalid-response.json b/adapters/telaria/telariatest/supplemental/invalid-response.json new file mode 100644 index 00000000000..8e4f1ee8cb5 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/invalid-response.json @@ -0,0 +1,105 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"], + "X-Openrtb-Version": ["2.5"], + "Accept-Encoding": ["gzip"], + "User-Agent": ["test-user-agent"], + "X-Forwarded-For": ["123.123.123.123"], + "Accept-Language": ["en"], + "Dnt": ["0"] + }, + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Telaria: Bad Server Response", + "comparison": "literal" + } + ] +} diff --git a/adapters/telaria/telariatest/supplemental/invalid-telaria-ext-object.json b/adapters/telaria/telariatest/supplemental/invalid-telaria-ext-object.json new file mode 100644 index 00000000000..efdec8ad61b --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/invalid-telaria-ext-object.json @@ -0,0 +1,29 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "Telaria: ext.bidder not provided", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": "Awesome" + } + ], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/telaria/telariatest/supplemental/requires-imp-object.json b/adapters/telaria/telariatest/supplemental/requires-imp-object.json new file mode 100644 index 00000000000..5933dc4ee08 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/requires-imp-object.json @@ -0,0 +1,16 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "Telaria: Missing Imp Object", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/telaria/telariatest/supplemental/requires-seat-code.json b/adapters/telaria/telariatest/supplemental/requires-seat-code.json new file mode 100644 index 00000000000..5c05e529772 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/requires-seat-code.json @@ -0,0 +1,30 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "Telaria: Seat Code required", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-1", + "video": { + "w": 640, + "h": 480, + "linearity": 1 + }, + "ext": { + "bidder": { + "adCode": "my-adcode" + } + } + } + ], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/telaria/telariatest/supplemental/requires-video-object.json b/adapters/telaria/telariatest/supplemental/requires-video-object.json new file mode 100644 index 00000000000..3f797c9e1de --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/requires-video-object.json @@ -0,0 +1,26 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "Telaria: Only Supports Video", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-impression-1", + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/telaria/telariatest/supplemental/status-code-bad-request.json b/adapters/telaria/telariatest/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..0b5d8a85982 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/status-code-bad-request.json @@ -0,0 +1,80 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "" + } + } + ], + "app": { + "id": "123456789", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 400 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Telaria: Unexpected status code: [ 400 ] ", + "comparison": "literal" + } + ] +} diff --git a/adapters/telaria/telariatest/supplemental/status-code-no-content.json b/adapters/telaria/telariatest/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..ffb183f4121 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/status-code-no-content.json @@ -0,0 +1,83 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 204 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Telaria: Invalid Bid Request received by the server", + "comparison": "literal" + } + ] +} diff --git a/adapters/telaria/telariatest/supplemental/status-code-other-error.json b/adapters/telaria/telariatest/supplemental/status-code-other-error.json new file mode 100644 index 00000000000..15e4b7f87d8 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/status-code-other-error.json @@ -0,0 +1,83 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 306 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Telaria: Something went wrong, please contact your Account Manager. Status Code: [ 306 ] ", + "comparison": "literal" + } + ] +} diff --git a/adapters/telaria/telariatest/supplemental/status-code-service-unavailable.json b/adapters/telaria/telariatest/supplemental/status-code-service-unavailable.json new file mode 100644 index 00000000000..b92d4ea8ba1 --- /dev/null +++ b/adapters/telaria/telariatest/supplemental/status-code-service-unavailable.json @@ -0,0 +1,83 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "adCode": "my-adcode", + "seatCode": "my-seatcode" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 503 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Telaria: Something went wrong, please contact your Account Manager. Status Code: [ 503 ] ", + "comparison": "literal" + } + ] +} diff --git a/adapters/telaria/usersync.go b/adapters/telaria/usersync.go new file mode 100644 index 00000000000..e3f76f6e9b4 --- /dev/null +++ b/adapters/telaria/usersync.go @@ -0,0 +1,12 @@ +package telaria + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewTelariaSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("telaria", 202, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/telaria/usersync_test.go b/adapters/telaria/usersync_test.go new file mode 100644 index 00000000000..4896b253d2f --- /dev/null +++ b/adapters/telaria/usersync_test.go @@ -0,0 +1,33 @@ +package telaria + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestTelariaSyncer(t *testing.T) { + + syncURL := "https://pbs.publishers.tremorhub.com/pubsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewTelariaSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://pbs.publishers.tremorhub.com/pubsync?gdpr=0&gdpr_consent=", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 202, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) + assert.Equal(t, "telaria", syncer.FamilyName()) + +} diff --git a/config/config.go b/config/config.go index 2cb5f8f2e66..652ad28cd87 100644 --- a/config/config.go +++ b/config/config.go @@ -535,6 +535,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSynacormedia, "https://sync.technoratimedia.com/services?srv=cs&pid=70&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsynacormedia%26uid%3D%5BUSER_ID%5D") // openrtb_ext.BidderTappx doesn't have a good default. + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTelaria, "https://pbs.publishers.tremorhub.com/pubsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtelaria%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Btvid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUcfunnel, "https://sync.aralego.com/idsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&usprivacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId") @@ -729,6 +730,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.sovrn.endpoint", "http://ap.lijit.com/rtb/bid?src=prebid_server") v.SetDefault("adapters.synacormedia.endpoint", "http://{{.Host}}.technoratimedia.com/openrtb/bids/{{.Host}}") v.SetDefault("adapters.tappx.endpoint", "https://{{.Host}}") + v.SetDefault("adapters.telaria.endpoint", "https://ads.tremorhub.com/ad/rtb/prebid") v.SetDefault("adapters.triplelift_native.disabled", true) v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}") v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index f7b970c571b..8e779822cae 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -54,6 +54,7 @@ import ( "github.com/prebid/prebid-server/adapters/sovrn" "github.com/prebid/prebid-server/adapters/synacormedia" "github.com/prebid/prebid-server/adapters/tappx" + "github.com/prebid/prebid-server/adapters/telaria" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" "github.com/prebid/prebid-server/adapters/ucfunnel" @@ -127,6 +128,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderSovrn: sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint), openrtb_ext.BidderSynacormedia: synacormedia.NewSynacorMediaBidder(cfg.Adapters[string(openrtb_ext.BidderSynacormedia)].Endpoint), openrtb_ext.BidderTappx: tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint), + openrtb_ext.BidderTelaria: telaria.NewTelariaBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTelaria))].Endpoint), openrtb_ext.BidderTriplelift: triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint), openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo), openrtb_ext.BidderUcfunnel: ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index ec9745563ef..43d9b894ea8 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -72,6 +72,7 @@ const ( BidderSovrn BidderName = "sovrn" BidderSynacormedia BidderName = "synacormedia" BidderTappx BidderName = "tappx" + BidderTelaria BidderName = "telaria" BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" BidderUcfunnel BidderName = "ucfunnel" @@ -135,6 +136,7 @@ var BidderMap = map[string]BidderName{ "sovrn": BidderSovrn, "synacormedia": BidderSynacormedia, "tappx": BidderTappx, + "telaria": BidderTelaria, "triplelift": BidderTriplelift, "triplelift_native": BidderTripleliftNative, "ucfunnel": BidderUcfunnel, diff --git a/openrtb_ext/imp_telaria.go b/openrtb_ext/imp_telaria.go new file mode 100644 index 00000000000..8ea371a8ad0 --- /dev/null +++ b/openrtb_ext/imp_telaria.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtImpTelaria struct { + AdCode string `json:"adCode,omitempty"` + SeatCode string `json:"seatCode"` +} diff --git a/static/bidder-info/telaria.yaml b/static/bidder-info/telaria.yaml new file mode 100644 index 00000000000..43e8707a17b --- /dev/null +++ b/static/bidder-info/telaria.yaml @@ -0,0 +1,9 @@ +maintainer: + email: "github@telaria.com" +capabilities: + app: + mediaTypes: + - video + site: + mediaTypes: + - video diff --git a/static/bidder-params/telaria.json b/static/bidder-params/telaria.json new file mode 100644 index 00000000000..b4121967351 --- /dev/null +++ b/static/bidder-params/telaria.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Telaria Adapter Params", + "description": "A schema which validates params accepted by the Telaria adapter", + + "type": "object", + "properties": { + "adCode": { + "type": "string", + "description": "The Ad Unit Code." + }, + "seatCode": { + "type": "string", + "description": "Your Seat Code." + }, + "originalPublisherid": { + "type": "string", + "description": "publisher ID from the original request" + } + }, + "required": ["seatCode"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index be0392f2dbb..da235402bf0 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -47,6 +47,7 @@ import ( "github.com/prebid/prebid-server/adapters/sonobi" "github.com/prebid/prebid-server/adapters/sovrn" "github.com/prebid/prebid-server/adapters/synacormedia" + "github.com/prebid/prebid-server/adapters/telaria" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" "github.com/prebid/prebid-server/adapters/ucfunnel" @@ -110,6 +111,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSmartRTB, smartrtb.NewSmartRTBSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderTelaria, telaria.NewTelariaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderUcfunnel, ucfunnel.NewUcfunnelSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 383e24d82cf..637f590e25f 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -56,6 +56,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderSovrn): syncConfig, string(openrtb_ext.BidderSmartRTB): syncConfig, string(openrtb_ext.BidderSynacormedia): syncConfig, + string(openrtb_ext.BidderTelaria): syncConfig, string(openrtb_ext.BidderTriplelift): syncConfig, string(openrtb_ext.BidderTripleliftNative): syncConfig, string(openrtb_ext.BidderUcfunnel): syncConfig, From f07b1c335893b7c80a2c58844031885aece8b85b Mon Sep 17 00:00:00 2001 From: Krzysztof Desput Date: Wed, 15 Apr 2020 21:03:10 +0200 Subject: [PATCH 053/318] #615 Beachfront URLs from config (#1238) --- adapters/beachfront/beachfront.go | 80 ++++++++++++------- adapters/beachfront/beachfront_test.go | 2 +- .../exemplary/minimal-banner.json | 2 +- .../beachfronttest/exemplary/simple-mix.json | 2 +- .../minimal-banner-empty_array-200.json | 2 +- .../supplemental/minimal-site-banner.json | 2 +- .../supplemental/mobile-banner.json | 2 +- .../supplemental/multi-banner.json | 2 +- config/config.go | 1 + config/config_test.go | 2 + exchange/adapter_map.go | 17 ++-- exchange/exchange_test.go | 5 ++ 12 files changed, 74 insertions(+), 45 deletions(-) diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go index e2eb31b3577..34a198e93a2 100644 --- a/adapters/beachfront/beachfront.go +++ b/adapters/beachfront/beachfront.go @@ -4,26 +4,27 @@ import ( "encoding/json" "errors" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" "net/http" "reflect" "strconv" "strings" + + "github.com/golang/glog" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" ) const Seat = "beachfront" const BidCapacity = 5 -const bannerEndpoint = "https://display.bfmio.com/prebid_display" -const videoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id" +const defaultVideoEndpoint = "https://reachms.bfmio.com/bid.json?exchange_id" const nurlVideoEndpointSuffix = "&prebidserver" const beachfrontAdapterName = "BF_PREBID_S2S" -const beachfrontAdapterVersion = "0.8.0" +const beachfrontAdapterVersion = "0.9.0" const minBidFloor = 0.01 @@ -31,6 +32,12 @@ const DefaultVideoWidth = 300 const DefaultVideoHeight = 250 type BeachfrontAdapter struct { + bannerEndpoint string + extraInfo ExtraInfo +} + +type ExtraInfo struct { + VideoEndpoint string `json:"video_endpoint,omitempty"` } type beachfrontRequests struct { @@ -138,7 +145,7 @@ func (a *BeachfrontAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *a if err == nil { reqs[0] = &adapters.RequestData{ Method: "POST", - Uri: bannerEndpoint, + Uri: a.bannerEndpoint, Body: bytes, Headers: headers, } @@ -159,7 +166,7 @@ func (a *BeachfrontAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *a if err == nil { reqs[j+nurlBump] = &adapters.RequestData{ Method: "POST", - Uri: videoEndpoint + "=" + beachfrontRequests.ADMVideo[j].AppId, + Uri: a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.ADMVideo[j].AppId, Body: bytes, Headers: headers, } @@ -178,7 +185,7 @@ func (a *BeachfrontAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *a bytes = append([]byte(`{"isPrebid":true,`), bytes[1:]...) reqs[j+admBump] = &adapters.RequestData{ Method: "POST", - Uri: videoEndpoint + "=" + beachfrontRequests.NurlVideo[j].AppId + nurlVideoEndpointSuffix, + Uri: a.extraInfo.VideoEndpoint + "=" + beachfrontRequests.NurlVideo[j].AppId + nurlVideoEndpointSuffix, Body: bytes, Headers: headers, } @@ -518,13 +525,13 @@ func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, extern bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &bids[i], - BidType: getBidType(externalRequest), + BidType: a.getBidType(externalRequest), BidVideo: &impVideo, }) } else { bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &bids[i], - BidType: getBidType(externalRequest), + BidType: a.getBidType(externalRequest), }) } } @@ -532,6 +539,15 @@ func (a *BeachfrontAdapter) MakeBids(internalRequest *openrtb.BidRequest, extern return bidResponse, errs } +func (a *BeachfrontAdapter) getBidType(externalRequest *adapters.RequestData) openrtb_ext.BidType { + t := strings.Split(externalRequest.Uri, "=")[0] + if t == a.extraInfo.VideoEndpoint { + return openrtb_ext.BidTypeVideo + } + + return openrtb_ext.BidTypeBanner +} + func postprocess(response *adapters.ResponseData, xtrnal openrtb.BidRequest, uri string, id string) ([]openrtb.Bid, []error) { var beachfrontResp []beachfrontResponseSlot var errs = make([]error, 0) @@ -629,13 +645,13 @@ func getBeachfrontExtension(imp openrtb.Imp) (openrtb_ext.ExtImpBeachfront, erro } func getDomain(page string) string { - protoUrl := strings.Split(page, "//") + protoURL := strings.Split(page, "//") var domainPage string - if len(protoUrl) > 1 { - domainPage = protoUrl[1] + if len(protoURL) > 1 { + domainPage = protoURL[1] } else { - domainPage = protoUrl[0] + domainPage = protoURL[0] } return strings.Split(domainPage, "/")[0] @@ -643,9 +659,9 @@ func getDomain(page string) string { } func isSecure(page string) int8 { - protoUrl := strings.Split(page, "://") + protoURL := strings.Split(page, "://") - if len(protoUrl) > 1 && protoUrl[0] == "https" { + if len(protoURL) > 1 && protoURL[0] == "https" { return 1 } @@ -663,19 +679,25 @@ func getIP(ip string) string { return ip } -func getBidType(externalRequest *adapters.RequestData) openrtb_ext.BidType { - t := strings.Split(externalRequest.Uri, "=")[0] - if t == videoEndpoint { - return openrtb_ext.BidTypeVideo - } - - return openrtb_ext.BidTypeBanner -} - func removeVideoElement(slice []beachfrontVideoRequest, s int) []beachfrontVideoRequest { return append(slice[:s], slice[s+1:]...) } -func NewBeachfrontBidder() *BeachfrontAdapter { - return &BeachfrontAdapter{} +func NewBeachfrontBidder(bannerEndpoint string, extraAdapterInfo string) adapters.Bidder { + var extraInfo ExtraInfo + + if len(extraAdapterInfo) == 0 { + extraAdapterInfo = "{\"video_endpoint\":\"" + defaultVideoEndpoint + "\"}" + } + + if err := json.Unmarshal([]byte(extraAdapterInfo), &extraInfo); err != nil { + glog.Fatal("Invalid Beachfront extra adapter info: " + err.Error()) + return nil + } + + if extraInfo.VideoEndpoint == "" { + extraInfo.VideoEndpoint = defaultVideoEndpoint + } + + return &BeachfrontAdapter{bannerEndpoint: bannerEndpoint, extraInfo: extraInfo} } diff --git a/adapters/beachfront/beachfront_test.go b/adapters/beachfront/beachfront_test.go index 4e82deaf3d8..c220cebf9b0 100644 --- a/adapters/beachfront/beachfront_test.go +++ b/adapters/beachfront/beachfront_test.go @@ -7,5 +7,5 @@ import ( ) func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "beachfronttest", new(BeachfrontAdapter)) + adapterstest.RunJSONBidderTest(t, "beachfronttest", NewBeachfrontBidder("https://display.bfmio.com/prebid_display", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}")) } diff --git a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json index ffcea194cdd..51ce4e9295e 100644 --- a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json +++ b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json @@ -56,7 +56,7 @@ "dnt": 0, "ua": "", "adapterName": "BF_PREBID_S2S", - "adapterVersion": "0.8.0", + "adapterVersion": "0.9.0", "user": { } } diff --git a/adapters/beachfront/beachfronttest/exemplary/simple-mix.json b/adapters/beachfront/beachfronttest/exemplary/simple-mix.json index 6d8e483ee6d..eb5d9b07abc 100644 --- a/adapters/beachfront/beachfronttest/exemplary/simple-mix.json +++ b/adapters/beachfront/beachfronttest/exemplary/simple-mix.json @@ -85,7 +85,7 @@ "buyeruid": "some-buyer" }, "adapterName": "BF_PREBID_S2S", - "adapterVersion": "0.8.0", + "adapterVersion": "0.9.0", "ip": "192.168.255.255", "requestId": "61b87329-8790-47b7-90dd-c53ae7ce1723" } diff --git a/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json b/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json index f189b2c8c79..7bdbc73cd5e 100644 --- a/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json +++ b/adapters/beachfront/beachfronttest/supplemental/minimal-banner-empty_array-200.json @@ -56,7 +56,7 @@ "dnt": 0, "ua": "", "adapterName": "BF_PREBID_S2S", - "adapterVersion": "0.8.0", + "adapterVersion": "0.9.0", "user": { } } diff --git a/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json b/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json index b610c96f58a..27b24357247 100644 --- a/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json +++ b/adapters/beachfront/beachfronttest/supplemental/minimal-site-banner.json @@ -56,7 +56,7 @@ "dnt": 0, "ua": "", "adapterName": "BF_PREBID_S2S", - "adapterVersion": "0.8.0", + "adapterVersion": "0.9.0", "user": { } } diff --git a/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json b/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json index d47393b7caf..ea38d7adae7 100644 --- a/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json +++ b/adapters/beachfront/beachfronttest/supplemental/mobile-banner.json @@ -87,7 +87,7 @@ }, "adapterName":"BF_PREBID_S2S", - "adapterVersion":"0.8.0", + "adapterVersion":"0.9.0", "ip":"192.168.255.255", "requestId":"763e3312-19d5-4b07-a61d-890147e863a1" } diff --git a/adapters/beachfront/beachfronttest/supplemental/multi-banner.json b/adapters/beachfront/beachfronttest/supplemental/multi-banner.json index c4120787852..46699511a9c 100644 --- a/adapters/beachfront/beachfronttest/supplemental/multi-banner.json +++ b/adapters/beachfront/beachfronttest/supplemental/multi-banner.json @@ -96,7 +96,7 @@ "dnt": 1, "ua": "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16", "adapterName": "BF_PREBID_S2S", - "adapterVersion": "0.8.0", + "adapterVersion": "0.9.0", "user": { "buyeruid": "some-buyer", "id": "some-user" diff --git a/config/config.go b/config/config.go index 652ad28cd87..1c686591ef2 100644 --- a/config/config.go +++ b/config/config.go @@ -695,6 +695,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs v.SetDefault("adapters.appnexus.platform_id", "5") v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display") + v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs") v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2") v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/s2s/header/24") diff --git a/config/config_test.go b/config/config_test.go index 9677ce2aaba..92794d7941e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -270,6 +270,8 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "adapters.audiencenetwork.usersync_url", cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].UserSyncURL, "http://facebook.com/ortb/prebid-s2s") cmpStrings(t, "adapters.audiencenetwork.platform_id", cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID, "abcdefgh1234") cmpStrings(t, "adapters.audiencenetwork.app_secret", cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret, "987abc") + cmpStrings(t, "adapters.beachfront.endpoint", cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, "https://display.bfmio.com/prebid_display") + cmpStrings(t, "adapters.beachfront.extra_info", cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo, "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") cmpStrings(t, "adapters.ix.endpoint", cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderIx))].Endpoint, "http://ixtest.com/api") cmpStrings(t, "adapters.rubicon.endpoint", cfg.Adapters[string(openrtb_ext.BidderRubicon)].Endpoint, "http://rubitest.com/api") cmpStrings(t, "adapters.rubicon.usersync_url", cfg.Adapters[string(openrtb_ext.BidderRubicon)].UserSyncURL, "http://pixel.rubiconproject.com/sync.php?p=prebid") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 8e779822cae..20805fb7898 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -85,15 +85,14 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdvangelists: advangelists.NewAdvangelistsBidder(cfg.Adapters[string(openrtb_ext.BidderAdvangelists)].Endpoint), openrtb_ext.BidderApplogy: applogy.NewApplogyBidder(cfg.Adapters[string(openrtb_ext.BidderApplogy)].Endpoint), openrtb_ext.BidderAppnexus: appnexus.NewAppNexusBidder(client, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].PlatformID), - // TODO #615: Update the config setup so that the Beachfront URLs can be configured, and use those in TestRaceIntegration in exchange_test.go - openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(), - openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), - openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), - openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), - openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), - openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), - openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), - openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), + openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo), + openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), + openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), + openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), + openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), + openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), + openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), + openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), openrtb_ext.BidderFacebook: audienceNetwork.NewFacebookBidder( client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID, diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index f263eea8569..e7df5e85733 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -483,6 +483,11 @@ func TestRaceIntegration(t *testing.T) { Endpoint: server.URL, PlatformID: "abc", } + cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderBeachfront))] = config.Adapter{ + Endpoint: server.URL, + ExtraAdapterInfo: "{\"video_endpoint\":\"" + server.URL + "\"}", + } + categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) From 76747ef3b2cb0cce51c624b2f3cb8161021952ab Mon Sep 17 00:00:00 2001 From: Mansi Nahar Date: Thu, 16 Apr 2020 10:35:27 -0400 Subject: [PATCH 054/318] Add nil check errors when setting native asset types (#1260) --- exchange/bidder.go | 39 ++++++++--- exchange/bidder_test.go | 144 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 11 deletions(-) diff --git a/exchange/bidder.go b/exchange/bidder.go index 8e95835ffba..7a53db5ee97 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -224,26 +224,43 @@ func addNativeTypes(bid *openrtb.Bid, request *openrtb.BidRequest) (*nativeRespo } for _, asset := range nativeMarkup.Assets { - setAssetTypes(asset, nativePayload) + if err := setAssetTypes(asset, nativePayload); err != nil { + errs = append(errs, err) + } } return nativeMarkup, errs } -func setAssetTypes(asset nativeResponse.Asset, nativePayload nativeRequests.Request) { +func setAssetTypes(asset nativeResponse.Asset, nativePayload nativeRequests.Request) error { if asset.Img != nil { - tempAsset := getAssetByID(asset.ID, nativePayload.Assets) - if tempAsset.Img.Type != 0 { - asset.Img.Type = tempAsset.Img.Type + if tempAsset, err := getAssetByID(asset.ID, nativePayload.Assets); err == nil { + if tempAsset.Img != nil { + if tempAsset.Img.Type != 0 { + asset.Img.Type = tempAsset.Img.Type + } + } else { + return fmt.Errorf("Response has an Image asset with ID:%d present that doesn't exist in the request", asset.ID) + } + } else { + return err } } if asset.Data != nil { - tempAsset := getAssetByID(asset.ID, nativePayload.Assets) - if tempAsset.Data.Type != 0 { - asset.Data.Type = tempAsset.Data.Type + if tempAsset, err := getAssetByID(asset.ID, nativePayload.Assets); err == nil { + if tempAsset.Data != nil { + if tempAsset.Data.Type != 0 { + asset.Data.Type = tempAsset.Data.Type + } + } else { + return fmt.Errorf("Response has a Data asset with ID:%d present that doesn't exist in the request", asset.ID) + } + } else { + return err } } + return nil } func getNativeImpByImpID(impID string, request *openrtb.BidRequest) (*openrtb.Native, error) { @@ -255,13 +272,13 @@ func getNativeImpByImpID(impID string, request *openrtb.BidRequest) (*openrtb.Na return nil, errors.New("Could not find native imp") } -func getAssetByID(id int64, assets []nativeRequests.Asset) nativeRequests.Asset { +func getAssetByID(id int64, assets []nativeRequests.Asset) (nativeRequests.Asset, error) { for _, asset := range assets { if id == asset.ID { - return asset + return asset, nil } } - return nativeRequests.Asset{} + return nativeRequests.Asset{}, fmt.Errorf("Unable to find asset with ID:%d in the request", id) } // makeExt transforms information about the HTTP call into the contract class for the PBS response. diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 46f63cc66c4..f20b431c13a 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -15,6 +15,9 @@ import ( "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" + + nativeRequests "github.com/mxmCherry/openrtb/native/request" + nativeResponse "github.com/mxmCherry/openrtb/native/response" ) // TestSingleBidder makes sure that the following things work if the Bidder needs only one request. @@ -1083,6 +1086,147 @@ func TestErrorReporting(t *testing.T) { } } +func TestSetAssetTypes(t *testing.T) { + testCases := []struct { + respAsset nativeResponse.Asset + nativeReq nativeRequests.Request + expectedErr string + desc string + }{ + { + respAsset: nativeResponse.Asset{ + ID: 1, + Img: &nativeResponse.Image{ + URL: "http://some-url", + }, + }, + nativeReq: nativeRequests.Request{ + Assets: []nativeRequests.Asset{ + { + ID: 1, + Img: &nativeRequests.Image{ + Type: 2, + }, + }, + { + ID: 2, + Data: &nativeRequests.Data{ + Type: 4, + }, + }, + }, + }, + expectedErr: "", + desc: "Matching image asset exists in the request and asset type is set correctly", + }, + { + respAsset: nativeResponse.Asset{ + ID: 2, + Data: &nativeResponse.Data{ + Label: "some label", + }, + }, + nativeReq: nativeRequests.Request{ + Assets: []nativeRequests.Asset{ + { + ID: 1, + Img: &nativeRequests.Image{ + Type: 2, + }, + }, + { + ID: 2, + Data: &nativeRequests.Data{ + Type: 4, + }, + }, + }, + }, + expectedErr: "", + desc: "Matching data asset exists in the request and asset type is set correctly", + }, + { + respAsset: nativeResponse.Asset{ + ID: 1, + Img: &nativeResponse.Image{ + URL: "http://some-url", + }, + }, + nativeReq: nativeRequests.Request{ + Assets: []nativeRequests.Asset{ + { + ID: 2, + Img: &nativeRequests.Image{ + Type: 2, + }, + }, + }, + }, + expectedErr: "Unable to find asset with ID:1 in the request", + desc: "Matching image asset with the same ID doesn't exist in the request", + }, + { + respAsset: nativeResponse.Asset{ + ID: 2, + Data: &nativeResponse.Data{ + Label: "some label", + }, + }, + nativeReq: nativeRequests.Request{ + Assets: []nativeRequests.Asset{ + { + ID: 2, + Img: &nativeRequests.Image{ + Type: 2, + }, + }, + }, + }, + expectedErr: "Response has a Data asset with ID:2 present that doesn't exist in the request", + desc: "Assets with same ID in the req and resp are of different types", + }, + { + respAsset: nativeResponse.Asset{ + ID: 1, + Img: &nativeResponse.Image{ + URL: "http://some-url", + }, + }, + nativeReq: nativeRequests.Request{ + Assets: []nativeRequests.Asset{ + { + ID: 1, + Data: &nativeRequests.Data{ + Type: 2, + }, + }, + }, + }, + expectedErr: "Response has an Image asset with ID:1 present that doesn't exist in the request", + desc: "Assets with same ID in the req and resp are of different types", + }, + } + + for _, test := range testCases { + err := setAssetTypes(test.respAsset, test.nativeReq) + if len(test.expectedErr) != 0 { + assert.EqualError(t, err, test.expectedErr, "Test Case: %s", test.desc) + continue + } else { + assert.NoError(t, err, "Test Case: %s", test.desc) + } + + for _, asset := range test.nativeReq.Assets { + if asset.Img != nil && test.respAsset.Img != nil { + assert.Equal(t, asset.Img.Type, test.respAsset.Img.Type, "Asset type not set correctly. Test Case: %s", test.desc) + } + if asset.Data != nil && test.respAsset.Data != nil { + assert.Equal(t, asset.Data.Type, test.respAsset.Data.Type, "Asset type not set correctly. Test Case: %s", test.desc) + } + } + } +} + type goodSingleBidder struct { bidRequest *openrtb.BidRequest httpRequest *adapters.RequestData From 79bb4dc745ae2220fe8baae04fa12a4c12b1827c Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Thu, 16 Apr 2020 07:55:47 -0700 Subject: [PATCH 055/318] Bugfix: no bids from bidder handling (#1252) Co-authored-by: Veronika Solovei --- exchange/exchange.go | 30 ++- exchange/exchange_test.go | 19 +- .../request-multi-bidders-debug-info.json | 227 ++++++++++++++++++ .../request-multi-bidders-one-no-resp.json | 122 ++++++++++ 4 files changed, 386 insertions(+), 12 deletions(-) create mode 100644 exchange/exchangetest/request-multi-bidders-debug-info.json create mode 100644 exchange/exchangetest/request-multi-bidders-one-no-resp.json diff --git a/exchange/exchange.go b/exchange/exchange.go index e625e5ca8f3..48903dd98c7 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -63,6 +63,9 @@ type exchange struct { type seatResponseExtra struct { ResponseTimeMillis int Errors []openrtb_ext.ExtBidderError + // httpCalls is the list of debugging info. It should only be populated if the request.test == 1. + // This will become response.ext.debug.httpcalls.{bidder} on the final Response. + HttpCalls []*openrtb_ext.ExtHttpCall } type bidResponseWrapper struct { @@ -324,6 +327,10 @@ func (e *exchange) getAllBids(ctx context.Context, cleanRequests map[openrtb_ext // Structure to record extra tracking data generated during bidding ae := new(seatResponseExtra) ae.ResponseTimeMillis = int(elapsed / time.Millisecond) + if bids != nil { + ae.HttpCalls = bids.httpCalls + } + // Timing statistics e.me.RecordAdapterTime(*bidlabels, time.Since(start)) serr := errsToBidderErrors(err) @@ -346,7 +353,12 @@ func (e *exchange) getAllBids(ctx context.Context, cleanRequests map[openrtb_ext // Wait for the bidders to do their thing for i := 0; i < len(cleanRequests); i++ { brw := <-chBids - adapterBids[brw.bidder] = brw.adapterBids + + //if bidder returned no bids back - remove bidder from further processing + if brw.adapterBids != nil && len(brw.adapterBids.bids) != 0 { + adapterBids[brw.bidder] = brw.adapterBids + } + //but we need to add all bidders data to adapterExtra to have metrics and other metadata adapterExtra[brw.bidder] = brw.adapterExtra if !bidsFound && adapterBids[brw.bidder] != nil && len(adapterBids[brw.bidder].bids) > 0 { @@ -639,20 +651,20 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb } } - for a, b := range adapterBids { - if b != nil && req.Test == 1 { - // Fill debug info - bidResponseExt.Debug.HttpCalls[a] = b.httpCalls + for bidderName, responseExtra := range adapterExtra { + + if req.Test == 1 { + bidResponseExt.Debug.HttpCalls[bidderName] = responseExtra.HttpCalls } // Only make an entry for bidder errors if the bidder reported any. - if len(adapterExtra[a].Errors) > 0 { - bidResponseExt.Errors[a] = adapterExtra[a].Errors + if len(responseExtra.Errors) > 0 { + bidResponseExt.Errors[bidderName] = responseExtra.Errors } if len(errList) > 0 { bidResponseExt.Errors[openrtb_ext.PrebidExtKey] = errsToBidderErrors(errList) } - bidResponseExt.ResponseTimeMillis[a] = adapterExtra[a].ResponseTimeMillis - // Defering the filling of bidResponseExt.Usersync[a] until later + bidResponseExt.ResponseTimeMillis[bidderName] = responseExtra.ResponseTimeMillis + // Defering the filling of bidResponseExt.Usersync[bidderName] until later } return bidResponseExt diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index e7df5e85733..3c1c2f3bc72 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -771,6 +771,11 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { } } } + if spec.IncomingRequest.OrtbRequest.Test == 1 { + //compare debug info + diffJson(t, "Debug info modified", bid.Ext, spec.Response.Ext) + + } } func findBiddersInAuction(t *testing.T, context string, req *openrtb.BidRequest) []string { @@ -1625,6 +1630,7 @@ type exchangeRequest struct { type exchangeResponse struct { Bids *openrtb.BidResponse `json:"bids"` Error string `json:"error,omitempty"` + Ext json.RawMessage `json:"ext,omitempty"` } type bidderSpec struct { @@ -1638,8 +1644,9 @@ type bidderRequest struct { } type bidderResponse struct { - SeatBid *bidderSeatBid `json:"pbsSeatBid,omitempty"` - Errors []string `json:"errors,omitempty"` + SeatBid *bidderSeatBid `json:"pbsSeatBid,omitempty"` + Errors []string `json:"errors,omitempty"` + HttpCalls []*openrtb_ext.ExtHttpCall `json:"httpCalls,omitempty"` } // bidderSeatBid is basically a subset of pbsOrtbSeatBid from exchange/bidder.go. @@ -1696,7 +1703,13 @@ func (b *validatingBidder) requestBid(ctx context.Context, request *openrtb.BidR } seatBid = &pbsOrtbSeatBid{ - bids: bids, + bids: bids, + httpCalls: mockResponse.HttpCalls, + } + } else { + seatBid = &pbsOrtbSeatBid{ + bids: nil, + httpCalls: mockResponse.HttpCalls, } } diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json new file mode 100644 index 00000000000..ec174f75b36 --- /dev/null +++ b/exchange/exchangetest/request-multi-bidders-debug-info.json @@ -0,0 +1,227 @@ +{ + "incomingRequest": { + "ortbRequest": { + "test": 1, + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 1 + }, + "audienceNetwork": { + "placementId": "some-placement" + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 2 + }, + "audienceNetwork": { + "placementId": "some-other-placement" + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "durationRangeSec": [ + 15, + 30 + ], + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withCategory": true, + "translateCategories": true + } + } + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "httpCalls": [ + { + "uri": "appnexusTest.com", + "requestbody": "appnexusTestRequestBody", + "responsebody": "appnexusTestResponseBody", + "status": 200 + } + ], + "pbsSeatBid": { + "pbsBids": [ + { + "ortbBid": { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 12.00, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + } + } + ] + } + } + }, + "audienceNetwork": { + "mockResponse": { + "httpCalls": [ + { + "uri": "audienceNetworkTest.com", + "requestbody": "audienceNetworkTestRequestBody", + "responsebody": "audienceNetworkTestResponseBody", + "status": 200 + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 12.00, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ], + "ext": { + "prebid": { + "type": "", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_size": "200x250", + "hb_size_appnexus": "200x250", + "hb_pb": "12.00", + "hb_pb_appnexus": "12.00", + "hb_pb_cat_dur": "12.00_VideoGames_15s", + "hb_pb_cat_dur_appnex": "12.00_VideoGames_15s" + } + } + } + } + ] + } + ] + }, + "ext": { + "debug": { + "httpcalls": { + "appnexus": [ + { + "uri": "appnexusTest.com", + "requestbody": "appnexusTestRequestBody", + "responsebody": "appnexusTestResponseBody", + "status": 200 + } + ], + "audienceNetwork": [ + { + "uri": "audienceNetworkTest.com", + "requestbody": "audienceNetworkTestRequestBody", + "responsebody": "audienceNetworkTestResponseBody", + "status": 200 + } + ] + }, + "resolvedrequest": { + "id": "some-request-id", + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 1 + }, + "audienceNetwork": { + "placementId": "some-placement" + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 2 + }, + "audienceNetwork": { + "placementId": "some-other-placement" + } + } + } + ], + "site": { + "page": "test.somepage.com" + }, + "test": 1, + "ext": { + "prebid": { + "targeting": { + "durationRangeSec": [ + 15, + 30 + ], + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withCategory": true, + "translateCategories": true + } + } + } + } + } + } + } + } +} + + diff --git a/exchange/exchangetest/request-multi-bidders-one-no-resp.json b/exchange/exchangetest/request-multi-bidders-one-no-resp.json new file mode 100644 index 00000000000..b7179ccb02e --- /dev/null +++ b/exchange/exchangetest/request-multi-bidders-one-no-resp.json @@ -0,0 +1,122 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + }, + "audienceNetwork": { + "placementId": "some-placement" + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 2 + }, + "audienceNetwork": { + "placementId": "some-other-placement" + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "durationRangeSec": [15,30], + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withCategory": true, + "translateCategories": true + } + } + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [ + { + "ortbBid": { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 12.00, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + } + } + ] + } + } + }, + "audienceNetwork": { + "mockResponse": { + + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 12.00, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": ["IAB1-1"], + "ext": { + "prebid": { + "type": "", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_size": "200x250", + "hb_size_appnexus": "200x250", + "hb_pb": "12.00", + "hb_pb_appnexus": "12.00", + "hb_pb_cat_dur": "12.00_VideoGames_15s", + "hb_pb_cat_dur_appnex": "12.00_VideoGames_15s" + } + } + } + }] + } + ] + } + } +} + + From 8657ae9b0113855e7dd15f921b3cf6f0387a63c3 Mon Sep 17 00:00:00 2001 From: jmaynardxandr <46759873+jmaynardxandr@users.noreply.github.com> Date: Tue, 21 Apr 2020 12:17:38 -0700 Subject: [PATCH 056/318] Add missing categories to AppNexus -> IAB mapping file. (#1264) * Add missing categories to AppNexus -> IAB mapping file. * Remove entry for category 38 which was set to a primary IAB category instead of a sub-category. * Fix order of category 22 --- static/adapter/appnexus/opts.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/adapter/appnexus/opts.json b/static/adapter/appnexus/opts.json index bd6f8af3e8b..3ea849735de 100644 --- a/static/adapter/appnexus/opts.json +++ b/static/adapter/appnexus/opts.json @@ -20,8 +20,11 @@ "19": "IAB18-4", "20": "IAB1-5", "21": "IAB1-6", + "22": "IAB19-28", "23": "IAB19-13", "24": "IAB22-2", + "25": "IAB3-9", + "26": "IAB17-26", "27": "IAB19-6", "28": "IAB1-7", "29": "IAB9-5", @@ -31,7 +34,6 @@ "33": "IAB16-5", "34": "IAB19-34", "37": "IAB11-4", - "38": "IAB23", "39": "IAB9-30", "41": "IAB7-44", "51": "IAB17-12", From dc9335ca0a343d4ccc89def2307562a6576c86df Mon Sep 17 00:00:00 2001 From: hbanalytics <55453525+hbanalytics@users.noreply.github.com> Date: Wed, 22 Apr 2020 17:11:38 +0300 Subject: [PATCH 057/318] Yieldone s2s Bid Adapter (#1242) * Added new Yieldone Bid s2s Adapter * Update endpoint for yieldone bid adapter * Fixes after review for Yieldone Bid s2s Adapter * Fix typeo in Yieldone s2s Bid Adapter --- adapters/yieldone/params_test.go | 48 ++++++ adapters/yieldone/usersync.go | 12 ++ adapters/yieldone/usersync_test.go | 30 ++++ adapters/yieldone/yieldone.go | 144 ++++++++++++++++++ adapters/yieldone/yieldone_test.go | 11 ++ .../yieldonetest/exemplary/simple-banner.json | 89 +++++++++++ .../yieldonetest/exemplary/simple-video.json | 87 +++++++++++ .../yieldonetest/params/race/banner.json | 4 + .../supplemental/bad_response.json | 65 ++++++++ .../yieldonetest/supplemental/status_204.json | 60 ++++++++ .../yieldonetest/supplemental/status_400.json | 65 ++++++++ .../yieldonetest/supplemental/status_418.json | 65 ++++++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_yieldone.go | 6 + static/bidder-info/yieldone.yaml | 11 ++ static/bidder-params/yieldone.json | 15 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 20 files changed, 721 insertions(+) create mode 100644 adapters/yieldone/params_test.go create mode 100644 adapters/yieldone/usersync.go create mode 100644 adapters/yieldone/usersync_test.go create mode 100644 adapters/yieldone/yieldone.go create mode 100644 adapters/yieldone/yieldone_test.go create mode 100644 adapters/yieldone/yieldonetest/exemplary/simple-banner.json create mode 100644 adapters/yieldone/yieldonetest/exemplary/simple-video.json create mode 100644 adapters/yieldone/yieldonetest/params/race/banner.json create mode 100644 adapters/yieldone/yieldonetest/supplemental/bad_response.json create mode 100644 adapters/yieldone/yieldonetest/supplemental/status_204.json create mode 100644 adapters/yieldone/yieldonetest/supplemental/status_400.json create mode 100644 adapters/yieldone/yieldonetest/supplemental/status_418.json create mode 100644 openrtb_ext/imp_yieldone.go create mode 100644 static/bidder-info/yieldone.yaml create mode 100644 static/bidder-params/yieldone.json diff --git a/adapters/yieldone/params_test.go b/adapters/yieldone/params_test.go new file mode 100644 index 00000000000..6048ea5d7dc --- /dev/null +++ b/adapters/yieldone/params_test.go @@ -0,0 +1,48 @@ +package yieldone + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderYieldone, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Yieldone params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderYieldone, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placementId": "123"}`, +} + +var invalidParams = []string{ + `null`, + `nil`, + ``, + `[]`, + `true`, + `2`, + `{"invalid_param": "123"}`, + `{"placementId": 123}`, +} diff --git a/adapters/yieldone/usersync.go b/adapters/yieldone/usersync.go new file mode 100644 index 00000000000..bc9d1b3235b --- /dev/null +++ b/adapters/yieldone/usersync.go @@ -0,0 +1,12 @@ +package yieldone + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewYieldoneSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("yieldone", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/yieldone/usersync_test.go b/adapters/yieldone/usersync_test.go new file mode 100644 index 00000000000..902f3b66b34 --- /dev/null +++ b/adapters/yieldone/usersync_test.go @@ -0,0 +1,30 @@ +package yieldone + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestYieldoneSyncer(t *testing.T) { + syncURL := "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewYieldoneSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "//not_localhost/synclocalhost%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/adapters/yieldone/yieldone.go b/adapters/yieldone/yieldone.go new file mode 100644 index 00000000000..f02d1a0b088 --- /dev/null +++ b/adapters/yieldone/yieldone.go @@ -0,0 +1,144 @@ +package yieldone + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type YieldoneAdapter struct { + endpoint string +} + +// MakeRequests makes the HTTP requests which should be made to fetch bids. +func (a *YieldoneAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errors = make([]error, 0) + + var validImps []openrtb.Imp + for i := 0; i < len(request.Imp); i++ { + if err := preprocess(&request.Imp[i]); err == nil { + validImps = append(validImps, request.Imp[i]) + } else { + errors = append(errors, err) + } + } + + request.Imp = validImps + + reqJSON, err := json.Marshal(request) + if err != nil { + errors = append(errors, err) + return nil, errors + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.endpoint, + Body: reqJSON, + Headers: headers, + }}, errors +} + +// MakeBids unpacks the server's response into Bids. +func (a *YieldoneAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + if err != nil { + return nil, []error{err} + } + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + }) + } + } + return bidResponse, nil + +} + +// NewYieldoneBidder configure bidder endpoint +func NewYieldoneBidder(endpoint string) *YieldoneAdapter { + return &YieldoneAdapter{ + endpoint: endpoint, + } +} + +func preprocess(imp *openrtb.Imp) error { + + var ext adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &ext); err != nil { + return err + } + var impressionExt openrtb_ext.ExtImpYieldone + if err := json.Unmarshal(ext.Bidder, &impressionExt); err != nil { + return err + } + + if imp.Banner != nil { + bannerCopy := *imp.Banner + if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 { + firstFormat := bannerCopy.Format[0] + bannerCopy.W = &(firstFormat.W) + bannerCopy.H = &(firstFormat.H) + } + imp.Banner = &bannerCopy + } + + return nil +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) { + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } + + if imp.Video != nil { + return openrtb_ext.BidTypeVideo, nil + } + + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unknown impression type for ID: \"%s\"", impID), + } + } + } + + // This shouldnt happen. Lets handle it just incase by returning an error. + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Failed to find impression for ID: \"%s\"", impID), + } +} diff --git a/adapters/yieldone/yieldone_test.go b/adapters/yieldone/yieldone_test.go new file mode 100644 index 00000000000..34d58bafbd7 --- /dev/null +++ b/adapters/yieldone/yieldone_test.go @@ -0,0 +1,11 @@ +package yieldone + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "yieldonetest", NewYieldoneBidder("http://localhost/prebid")) +} diff --git a/adapters/yieldone/yieldonetest/exemplary/simple-banner.json b/adapters/yieldone/yieldonetest/exemplary/simple-banner.json new file mode 100644 index 00000000000..f84476f1e86 --- /dev/null +++ b/adapters/yieldone/yieldonetest/exemplary/simple-banner.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [{ + "seat": "yieldone", + "bid": [{ + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300 + }] + }], + "cur": "JPY" + } + } + }], + + "expectedBidResponses": [{ + "currency": "JPY", + "bids": [{ + "bid": { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "12345678", + "cid": "987", + "crid": "12345678", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/yieldone/yieldonetest/exemplary/simple-video.json b/adapters/yieldone/yieldonetest/exemplary/simple-video.json new file mode 100644 index 00000000000..dc313abede7 --- /dev/null +++ b/adapters/yieldone/yieldonetest/exemplary/simple-video.json @@ -0,0 +1,87 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "41993" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "41993" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [{ + "seat": "yieldone", + "bid": [{ + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad-vast", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300 + }] + }], + "cur": "JPY" + } + } + }], + + "expectedBidResponses": [{ + "currency": "JPY", + "bids": [{ + "bid": { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad-vast", + "adid": "12345678", + "cid": "987", + "crid": "12345678", + "w": 300, + "h": 250 + }, + "type": "video" + }] + }] +} diff --git a/adapters/yieldone/yieldonetest/params/race/banner.json b/adapters/yieldone/yieldonetest/params/race/banner.json new file mode 100644 index 00000000000..c88180845eb --- /dev/null +++ b/adapters/yieldone/yieldonetest/params/race/banner.json @@ -0,0 +1,4 @@ +{ + "placementId": "36891" +} + diff --git a/adapters/yieldone/yieldonetest/supplemental/bad_response.json b/adapters/yieldone/yieldonetest/supplemental/bad_response.json new file mode 100644 index 00000000000..fa993a2fff5 --- /dev/null +++ b/adapters/yieldone/yieldonetest/supplemental/bad_response.json @@ -0,0 +1,65 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": "{\"id\"data.lost" + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/yieldone/yieldonetest/supplemental/status_204.json b/adapters/yieldone/yieldonetest/supplemental/status_204.json new file mode 100644 index 00000000000..b1c9304a35a --- /dev/null +++ b/adapters/yieldone/yieldonetest/supplemental/status_204.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/yieldone/yieldonetest/supplemental/status_400.json b/adapters/yieldone/yieldonetest/supplemental/status_400.json new file mode 100644 index 00000000000..1cb172bb371 --- /dev/null +++ b/adapters/yieldone/yieldonetest/supplemental/status_400.json @@ -0,0 +1,65 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/yieldone/yieldonetest/supplemental/status_418.json b/adapters/yieldone/yieldonetest/supplemental/status_418.json new file mode 100644 index 00000000000..30cc16adde5 --- /dev/null +++ b/adapters/yieldone/yieldonetest/supplemental/status_418.json @@ -0,0 +1,65 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://localhost/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": "36891" + } + } + } + ] + } + }, + "mockResponse": { + "status": 418, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 418. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/config/config.go b/config/config.go index 1c686591ef2..c944153a6b0 100644 --- a/config/config.go +++ b/config/config.go @@ -544,6 +544,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldone, "https://y.one.impact-ad.jp/hbs_sc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderZeroClickFraud, "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") } @@ -742,6 +743,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804") v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server") + v.SetDefault("adapters.yieldone.endpoint", "https://y.one.impact-ad.jp/hbs_imp") v.SetDefault("adapters.zeroclickfraud.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("max_request_size", 1024*256) diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 20805fb7898..585f89beb19 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -64,6 +64,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/yieldmo" + "github.com/prebid/prebid-server/adapters/yieldone" "github.com/prebid/prebid-server/adapters/zeroclickfraud" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" @@ -137,6 +138,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), openrtb_ext.BidderVrtcal: vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint), openrtb_ext.BidderYieldmo: yieldmo.NewYieldmoBidder(cfg.Adapters[string(openrtb_ext.BidderYieldmo)].Endpoint), + openrtb_ext.BidderYieldone: yieldone.NewYieldoneBidder(cfg.Adapters[string(openrtb_ext.BidderYieldone)].Endpoint), openrtb_ext.BidderZeroClickFraud: zeroclickfraud.NewZeroClickFraudBidder(cfg.Adapters[string(openrtb_ext.BidderZeroClickFraud)].Endpoint), } diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 43d9b894ea8..508379b5df9 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -82,6 +82,7 @@ const ( BidderVisx BidderName = "visx" BidderVrtcal BidderName = "vrtcal" BidderYieldmo BidderName = "yieldmo" + BidderYieldone BidderName = "yieldone" BidderZeroClickFraud BidderName = "zeroclickfraud" ) @@ -146,6 +147,7 @@ var BidderMap = map[string]BidderName{ "visx": BidderVisx, "vrtcal": BidderVrtcal, "yieldmo": BidderYieldmo, + "yieldone": BidderYieldone, "zeroclickfraud": BidderZeroClickFraud, } diff --git a/openrtb_ext/imp_yieldone.go b/openrtb_ext/imp_yieldone.go new file mode 100644 index 00000000000..6eee563b448 --- /dev/null +++ b/openrtb_ext/imp_yieldone.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpYieldone defines the contract for bidrequest.imp[i].ext.yieldone +type ExtImpYieldone struct { + PlacementId string `json:"placementId"` +} diff --git a/static/bidder-info/yieldone.yaml b/static/bidder-info/yieldone.yaml new file mode 100644 index 00000000000..74aef46d24f --- /dev/null +++ b/static/bidder-info/yieldone.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "y1dev@platform-one.co.jp" +capabilities: + site: + mediaTypes: + - banner + - video + app: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/yieldone.json b/static/bidder-params/yieldone.json new file mode 100644 index 00000000000..15d7acec177 --- /dev/null +++ b/static/bidder-params/yieldone.json @@ -0,0 +1,15 @@ + +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Yieldone Adapter Params", + "description": "A schema which validates params accepted by the Yieldone adapter", + + "type": "object", + "properties": { + "placementId": { + "type": "string", + "description": "Internal Yieldone Placement ID" + } + }, + "required": ["placementId"] + } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index da235402bf0..995f573adca 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -57,6 +57,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/yieldmo" + "github.com/prebid/prebid-server/adapters/yieldone" "github.com/prebid/prebid-server/adapters/zeroclickfraud" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" @@ -121,6 +122,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldone, yieldone.NewYieldoneSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderZeroClickFraud, zeroclickfraud.NewZeroClickFraudSyncer) return syncers diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 637f590e25f..c25a91bc6b4 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -66,6 +66,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderVisx): syncConfig, string(openrtb_ext.BidderVrtcal): syncConfig, string(openrtb_ext.BidderYieldmo): syncConfig, + string(openrtb_ext.BidderYieldone): syncConfig, string(openrtb_ext.BidderZeroClickFraud): syncConfig, }, } From b44980c8e53af42881ba7160b606c641c1730059 Mon Sep 17 00:00:00 2001 From: chino117 Date: Wed, 22 Apr 2020 11:15:24 -0300 Subject: [PATCH 058/318] Fix: URL de sync (#1261) --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index c944153a6b0..22c5b96222c 100644 --- a/config/config.go +++ b/config/config.go @@ -509,7 +509,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEPlanning, "https://ads.us.e-planning.net/uspd/1/?du=https%3A%2F%2Fads.us.e-planning.net%2Fgetuid%2F1%2F5a1ad71d2d53a0f5%3F"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEPlanning, "https://ads.us.e-planning.net/uspd/1/?du="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") // openrtb_ext.BidderFacebook doesn't have a good default. // openrtb_ext.BidderGamma doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGamoshi, "https://rtb.gamoshi.io/user_sync_prebid?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dgamoshi%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bgusr%5D") From 99dc46bc7eef9bf50b45ce34ad275b02425c0784 Mon Sep 17 00:00:00 2001 From: Aadesh Date: Wed, 22 Apr 2020 10:29:25 -0400 Subject: [PATCH 059/318] populate the app ID in the FAN timeout notif url with the publisher ID (#1265) and the auction with the request ID Co-authored-by: Aadesh Patel --- .../audienceNetworktest/exemplary/banner.json | 6 ++-- .../exemplary/interstitial.json | 6 ++-- .../exemplary/native-1.1.json | 6 ++-- .../audienceNetworktest/exemplary/video.json | 6 ++-- .../supplemental/banner-format-only.json | 6 ++-- .../supplemental/multi-imp.json | 12 +++---- .../supplemental/no-bid-204.json | 4 +-- .../supplemental/split-placementId.json | 6 ++-- adapters/audienceNetwork/facebook.go | 32 +++++++++++++++++-- adapters/audienceNetwork/facebook_test.go | 22 +++++++++++-- 10 files changed, 74 insertions(+), 32 deletions(-) diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json index 632629b53a2..f5f92515e26 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json @@ -51,7 +51,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -84,7 +84,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -92,7 +92,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json index 630e26d3f90..bad228d5f18 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json @@ -52,7 +52,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -86,7 +86,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -94,7 +94,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json index 288c7c14e5d..9090d80d099 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json @@ -45,7 +45,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -78,7 +78,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -86,7 +86,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json index 15563c2ada5..22c62f8b821 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json @@ -50,7 +50,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -88,7 +88,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -96,7 +96,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json index 52b7655593a..3edd6569258 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json @@ -53,7 +53,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -86,7 +86,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -94,7 +94,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json index 0fe836af4de..16e8aede10c 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json @@ -70,7 +70,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-1", "imp": [ { "id": "test-imp-1", @@ -103,7 +103,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "dfecd103a45daeb2a01728afb8ce78f6738f6007ecfebe1ca616b196e22b43e9", "platformid": "test-platform-id" } } @@ -111,7 +111,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-1", "seatbid": [ { "bid": [ @@ -147,7 +147,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-2", "imp": [ { "id": "test-imp-2", @@ -180,7 +180,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "a5fead11a4db86d0f62f57c3d8001640227120c8ef236549f0db010c1dbab399", "platformid": "test-platform-id" } } @@ -188,7 +188,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-2", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json index 042c86bd7fd..bb192aad76f 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json @@ -45,7 +45,7 @@ ] }, "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -78,7 +78,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json index b99834ab1df..4c561c55276 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json @@ -39,7 +39,7 @@ "expectedRequest": { "uri": "https://an.facebook.com/placementbid.ortb", "body": { - "id": "test-req-id", + "id": "test-imp-id", "imp": [ { "id": "test-imp-id", @@ -72,7 +72,7 @@ }, "tmax": 500, "ext": { - "authentication_id": "b2f9edfd707106adb6b692520081ad7e2a345444af1a895310228297a1b6247e", + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", "platformid": "test-platform-id" } } @@ -80,7 +80,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-req-id", + "id": "test-imp-id", "seatbid": [ { "bid": [ diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index db7657f59b7..9edb9a7d57e 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -130,6 +130,11 @@ func (this *FacebookAdapter) modifyRequest(out *openrtb.BidRequest) error { return err } + // Every outgoing FAN request has a single impression, so we can safely use the unique + // impression ID as the FAN request ID. We need to make sure that we update the request + // ID *BEFORE* we generate the auth ID since its a hash based on the request ID + out.ID = imp.ID + reqExt := facebookReqExt{ PlatformID: this.platformID, AuthID: this.makeAuthID(out), @@ -455,18 +460,39 @@ func NewFacebookBidder(client *http.Client, platformID string, appSecret string) } func (fa *FacebookAdapter) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error) { - // Note, facebook creates one request per imp, so all these requests will only have one imp in them - auction_id, err := jsonparser.GetString(req.Body, "imp", "[0]", "id") + var ( + rID string + pubID string + err error + ) + + // Note, the facebook adserver can only handle single impression requests, so we have to split multi-imp requests into + // multiple request. In order to ensure that every split request has a unique ID, the split request IDs are set to the + // corresponding imp's ID + rID, err = jsonparser.GetString(req.Body, "id") if err != nil { return &adapters.RequestData{}, []error{err} } - uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", fa.platformID, fa.platformID, auction_id) + // The publisher ID is either in the app object or the site object, depending on the supply of the request so we need + // to check both + pubID, err = jsonparser.GetString(req.Body, "app", "publisher", "id") + if err != nil { + pubID, err = jsonparser.GetString(req.Body, "site", "publisher", "id") + if err != nil { + return &adapters.RequestData{}, []error{ + errors.New("path [app|site].publisher.id not found in the request"), + } + } + } + + uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", fa.platformID, pubID, rID) timeoutReq := adapters.RequestData{ Method: "GET", Uri: uri, Body: nil, Headers: http.Header{}, } + return &timeoutReq, nil } diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go index 1edaabd45d7..784a540e596 100644 --- a/adapters/audienceNetwork/facebook_test.go +++ b/adapters/audienceNetwork/facebook_test.go @@ -43,9 +43,9 @@ func TestJsonSamples(t *testing.T) { adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder(nil, "test-platform-id", "test-app-secret")) } -func TestMakeTimeoutNotice(t *testing.T) { +func TestMakeTimeoutNoticeApp(t *testing.T) { req := adapters.RequestData{ - Body: []byte(`{"imp":[{"id":"1234"}]}}`), + Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"app":{"publisher":{"id":"5678"}}}`), } fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") @@ -56,9 +56,25 @@ func TestMakeTimeoutNotice(t *testing.T) { toReq, err := tb.MakeTimeoutNotification(&req) assert.Nil(t, err, "Facebook MakeTimeoutNotification() return an error %v", err) - expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=test-platform-id&auction=1234&ortb_loss_code=2" + expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=5678&auction=1234&ortb_loss_code=2" assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.") +} +func TestMakeTimeoutNoticeSite(t *testing.T) { + req := adapters.RequestData{ + Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"site":{"publisher":{"id":"5678"}}}`), + } + fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + + tb, ok := fba.(adapters.TimeoutBidder) + if !ok { + t.Error("Facebook adapter is not a TimeoutAdapter") + } + + toReq, err := tb.MakeTimeoutNotification(&req) + assert.Nil(t, err, "Facebook MakeTimeoutNotification() return an error %v", err) + expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=5678&auction=1234&ortb_loss_code=2" + assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.") } func TestMakeTimeoutNoticeBadRequest(t *testing.T) { From f4905e8fa74a6681091751d15c9c0e9de0252133 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Wed, 22 Apr 2020 07:30:58 -0700 Subject: [PATCH 060/318] Added header User Agent decoding (#1268) * Added header User Agent decoding * Added header User Agent decoding: unit tests * Added header User Agent decoding: unit tests * Added check UA is encoded to avoid `+` converted to space Co-authored-by: Veronika Solovei --- endpoints/openrtb2/video_auction.go | 16 +++++++-- endpoints/openrtb2/video_auction_test.go | 43 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 0215eb4cff2..c7316604d73 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -8,12 +8,13 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "strconv" "strings" "time" "github.com/buger/jsonparser" - jsonpatch "github.com/evanphx/json-patch" + "github.com/evanphx/json-patch" "github.com/gofrs/uuid" "github.com/prebid/prebid-server/errortypes" @@ -617,7 +618,18 @@ func (deps *endpointDeps) parseVideoRequest(request []byte, headers http.Header) //if Device.UA is not present in request body, init it with user-agent from request header if it's present if req.Device.UA == "" { - req.Device.UA = headers.Get("User-Agent") + ua := headers.Get("User-Agent") + + //Check UA is encoded. Without it the `+` character would get changed to a space if not actually encoded + if strings.ContainsAny(ua, "%") { + var err error + req.Device.UA, err = url.QueryUnescape(ua) + if err != nil { + req.Device.UA = ua + } + } else { + req.Device.UA = ua + } } errL, podErrors := deps.validateVideoRequest(req) diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index d0ce33de1c4..ec525c6ff08 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -927,6 +927,49 @@ func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) { } +func TestParseVideoRequestWithEncodedUserAgentInHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + uaEncoded := "Mozilla%2F5.0%20%28Macintosh%3B%20Intel%20Mac%20OS%20X%2010_14_6%29%20AppleWebKit%2F537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome%2F78.0.3904.87%20Safari%2F537.36" + uaDecoded := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + + headers := http.Header{} + headers.Add("User-Agent", uaEncoded) + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + +func TestParseVideoRequestWithDecodedUserAgentInHeader(t *testing.T) { + ex := &mockExchangeVideo{} + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample_without_device_user_agent.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + uaDecoded := "Mozilla/5.0+(Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36" + + headers := http.Header{} + headers.Add("User-Agent", uaDecoded) + + deps := mockDeps(t, ex) + req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + + assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header") + assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") + assert.Equal(t, make([]PodError, 0), podErr, "No pod errors should be returned") + +} + func TestHandleErrorDebugLog(t *testing.T) { vo := analytics.VideoObject{ Status: 200, From 62a135779b88ada41ac9872c2455a8d092fb02c4 Mon Sep 17 00:00:00 2001 From: Ad Generation Date: Fri, 24 Apr 2020 00:45:45 +0900 Subject: [PATCH 061/318] Ad Generation Adapter Integration. (#1253) * AdGeneration Integration. * update AdGeneration adapter. fix: some methods of the adgAdapter replace to functions. fix: unmarshal functions return a pointer. fix: header is defined once. fix: return when imps is appended * update AdGeneration Adapter. add: Added a comment in usersync. add: Added a test for parameters whose ID does not exist in params_test. change: Change to query creation by net/url. Added getRawQuery Test. fix: Changed variable names related to bidRequest. --- adapters/adgeneration/adgeneration.go | 260 ++++++++++++++++++ adapters/adgeneration/adgeneration_test.go | 176 ++++++++++++ .../exemplary/single-banner.json | 151 ++++++++++ .../adgenerationtest/params/race/banner.json | 3 + .../supplemental/204-bid-response.json | 72 +++++ .../supplemental/400-bid-response.json | 77 ++++++ .../supplemental/invalid-adg-param.json | 31 +++ .../supplemental/no-bid-response.json | 89 ++++++ adapters/adgeneration/params_test.go | 47 ++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adgeneration.go | 5 + static/bidder-info/adgeneration.yaml | 10 + static/bidder-params/adgeneration.json | 15 + usersync/usersyncers/syncer_test.go | 13 +- 16 files changed, 949 insertions(+), 6 deletions(-) create mode 100644 adapters/adgeneration/adgeneration.go create mode 100644 adapters/adgeneration/adgeneration_test.go create mode 100644 adapters/adgeneration/adgenerationtest/exemplary/single-banner.json create mode 100644 adapters/adgeneration/adgenerationtest/params/race/banner.json create mode 100644 adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json create mode 100644 adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json create mode 100644 adapters/adgeneration/adgenerationtest/supplemental/invalid-adg-param.json create mode 100644 adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json create mode 100644 adapters/adgeneration/params_test.go create mode 100644 openrtb_ext/imp_adgeneration.go create mode 100644 static/bidder-info/adgeneration.yaml create mode 100644 static/bidder-params/adgeneration.json diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go new file mode 100644 index 00000000000..4b1215dea9d --- /dev/null +++ b/adapters/adgeneration/adgeneration.go @@ -0,0 +1,260 @@ +package adgeneration + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type AdgenerationAdapter struct { + endpoint string + version string + defaultCurrency string +} + +// Server Responses +type adgServerResponse struct { + Locationid string `json:"locationid"` + Dealid string `json:"dealid"` + Ad string `json:"ad"` + Beacon string `json:"beacon"` + Beaconurl string `json:"beaconurl"` + Cpm float64 `jsons:"cpm"` + Creativeid string `json:"creativeid"` + H uint64 `json:"h"` + W uint64 `json:"w"` + Ttl uint64 `json:"ttl"` + Vastxml string `json:"vastxml,omitempty"` + LandingUrl string `json:"landing_url"` + Scheduleid string `json:"scheduleid"` + Results []interface{} `json:"results"` +} + +func (adg *AdgenerationAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + numRequests := len(request.Imp) + var errs []error + + if numRequests == 0 { + errs = append(errs, &errortypes.BadInput{ + Message: "No impression in the bid request", + }) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + bidRequestArray := make([]*adapters.RequestData, 0, numRequests) + + for index := 0; index < numRequests; index++ { + bidRequestUri, err := adg.getRequestUri(request, index) + if err != nil { + errs = append(errs, err) + return nil, errs + } + bidRequest := &adapters.RequestData{ + Method: "GET", + Uri: bidRequestUri, + Body: nil, + Headers: headers, + } + bidRequestArray = append(bidRequestArray, bidRequest) + } + + return bidRequestArray, errs +} + +func (adg *AdgenerationAdapter) getRequestUri(request *openrtb.BidRequest, index int) (string, error) { + imp := request.Imp[index] + adgExt, err := unmarshalExtImpAdgeneration(&imp) + if err != nil { + return "", &errortypes.BadInput{ + Message: err.Error(), + } + } + uriObj, err := url.Parse(adg.endpoint) + if err != nil { + return "", &errortypes.BadInput{ + Message: err.Error(), + } + } + v := adg.getRawQuery(adgExt.Id, request, &imp) + uriObj.RawQuery = v.Encode() + return uriObj.String(), err +} + +func (adg *AdgenerationAdapter) getRawQuery(id string, request *openrtb.BidRequest, imp *openrtb.Imp) *url.Values { + v := url.Values{} + v.Set("posall", "SSPLOC") + v.Set("id", id) + v.Set("sdktype", "0") + v.Set("hb", "true") + v.Set("t", "json3") + v.Set("currency", adg.getCurrency(request)) + v.Set("sdkname", "prebidserver") + v.Set("adapterver", adg.version) + adSize := getSizes(imp) + if adSize != "" { + v.Set("size", adSize) + } + if request.Site != nil && request.Site.Page != "" { + v.Set("tp", request.Site.Page) + } + return &v +} + +func unmarshalExtImpAdgeneration(imp *openrtb.Imp) (*openrtb_ext.ExtImpAdgeneration, error) { + var bidderExt adapters.ExtImpBidder + var adgExt openrtb_ext.ExtImpAdgeneration + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, err + } + if err := json.Unmarshal(bidderExt.Bidder, &adgExt); err != nil { + return nil, err + } + if adgExt.Id == "" { + return nil, errors.New("No Location ID in ExtImpAdgeneration.") + } + return &adgExt, nil +} + +func getSizes(imp *openrtb.Imp) string { + if imp.Banner == nil || len(imp.Banner.Format) == 0 { + return "" + } + var sizeStr string + for _, v := range imp.Banner.Format { + sizeStr += strconv.FormatUint(v.W, 10) + "×" + strconv.FormatUint(v.H, 10) + "," + } + if len(sizeStr) > 0 && strings.LastIndex(sizeStr, ",") == len(sizeStr)-1 { + sizeStr = sizeStr[:len(sizeStr)-1] + } + return sizeStr +} + +func (adg *AdgenerationAdapter) getCurrency(request *openrtb.BidRequest) string { + if len(request.Cur) <= 0 { + return adg.defaultCurrency + } else { + for _, c := range request.Cur { + if adg.defaultCurrency == c { + return c + } + } + return request.Cur[0] + } +} + +func (adg *AdgenerationAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + var bidResp adgServerResponse + err := json.Unmarshal(response.Body, &bidResp) + if err != nil { + return nil, []error{err} + } + if len(bidResp.Results) <= 0 { + return nil, nil + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + var impId string + var bitType openrtb_ext.BidType + var adm string + for _, v := range internalRequest.Imp { + adgExt, err := unmarshalExtImpAdgeneration(&v) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }, + } + } + if adgExt.Id == bidResp.Locationid { + impId = v.ID + bitType = openrtb_ext.BidTypeBanner + adm = createAd(&bidResp, impId) + bid := openrtb.Bid{ + ID: bidResp.Locationid, + ImpID: impId, + AdM: adm, + Price: bidResp.Cpm, + W: bidResp.W, + H: bidResp.H, + CrID: bidResp.Creativeid, + DealID: bidResp.Dealid, + } + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bitType, + }) + return bidResponse, nil + } + } + return nil, nil +} + +func createAd(body *adgServerResponse, impId string) string { + ad := body.Ad + if body.Vastxml != "" { + ad = "
" + insertVASTMethod(impId, body.Vastxml) + "" + } + ad = appendChildToBody(ad, body.Beacon) + unwrappedAd := removeWrapper(ad) + if unwrappedAd != "" { + return unwrappedAd + } + return ad +} + +func insertVASTMethod(bidId string, vastxml string) string { + rep := regexp.MustCompile(`/\r?\n/g`) + var replacedVastxml = rep.ReplaceAllString(vastxml, "") + return "" +} + +func appendChildToBody(ad string, data string) string { + rep := regexp.MustCompile(`<\/\s?body>`) + return rep.ReplaceAllString(ad, data+"") +} + +func removeWrapper(ad string) string { + bodyIndex := strings.Index(ad, "") + lastBodyIndex := strings.LastIndex(ad, "") + if bodyIndex == -1 || lastBodyIndex == -1 { + return "" + } + + str := strings.TrimSpace(strings.Replace(strings.Replace(ad[bodyIndex:lastBodyIndex], "", "", 1), "", "", 1)) + return str +} + +func NewAdgenerationAdapter(endpoint string) *AdgenerationAdapter { + return &AdgenerationAdapter{ + endpoint, + "1.0.0", + "JPY", + } +} diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go new file mode 100644 index 00000000000..e76995fc5e4 --- /dev/null +++ b/adapters/adgeneration/adgeneration_test.go @@ -0,0 +1,176 @@ +package adgeneration + +import ( + "encoding/json" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "adgenerationtest", NewAdgenerationAdapter("https://d.socdm.com/adsv/v1")) +} + +func TestgetRequestUri(t *testing.T) { + bidder := NewAdgenerationAdapter("https://d.socdm.com/adsv/v1") + // Test items + failedRequest := &openrtb.BidRequest{ + ID: "test-failed-bid-request", + Imp: []openrtb.Imp{ + {ID: "extImpBidder-failed-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{{ "id": "58278" }}`)}, + {ID: "extImpBidder-failed-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{"_bidder": { "id": "58278" }}`)}, + {ID: "extImpAdgeneration-failed-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{"bidder": { "_id": "58278" }}`)}, + }, + Device: &openrtb.Device{UA: "testUA", IP: "testIP"}, + Site: &openrtb.Site{Page: "https://supership.com"}, + User: &openrtb.User{BuyerUID: "buyerID"}, + } + successRequest := &openrtb.BidRequest{ + ID: "test-success-bid-request", + Imp: []openrtb.Imp{ + {ID: "bidRequest-success-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{"bidder": { "id": "58278" }}`)}, + }, + Device: &openrtb.Device{UA: "testUA", IP: "testIP"}, + Site: &openrtb.Site{Page: "https://supership.com"}, + User: &openrtb.User{BuyerUID: "buyerID"}, + } + + numRequests := len(failedRequest.Imp) + for index := 0; index < numRequests; index++ { + httpRequests, err := bidder.getRequestUri(failedRequest, index) + if err == nil { + t.Errorf("getRequestUri: %v did not throw an error", failedRequest.Imp[index]) + } + if httpRequests != "" { + t.Errorf("getRequestUri: %v did return Request: %s", failedRequest.Imp[index], httpRequests) + } + } + numRequests = len(successRequest.Imp) + for index := 0; index < numRequests; index++ { + // RequestUri Test. + httpRequests, err := bidder.getRequestUri(successRequest, index) + if err != nil { + t.Errorf("getRequestUri: %v did throw an error: %v", successRequest.Imp[index], err) + } + if httpRequests == "adapterver="+bidder.version+"¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html" { + t.Errorf("getRequestUri: %v did return Request: %s", successRequest.Imp[index], httpRequests) + } + // getRawQuery Test. + adgExt, err := unmarshalExtImpAdgeneration(&successRequest.Imp[index]) + if err != nil { + t.Errorf("unmarshalExtImpAdgeneration: %v did throw an error: %v", successRequest.Imp[index], err) + } + rawQuery := bidder.getRawQuery(adgExt.Id, successRequest, &successRequest.Imp[index]) + expectQueries := map[string]string{ + "posall": "SSPLOC", + "id": adgExt.Id, + "sdktype": "0", + "hb": "true", + "currency": bidder.getCurrency(successRequest), + "sdkname": "prebidserver", + "adapterver": bidder.version, + "size": getSizes(&successRequest.Imp[index]), + "tp": successRequest.Site.Name, + } + for key, expectedValue := range expectQueries { + actualValue := rawQuery.Get(key) + if actualValue == "" { + if !(key == "size" || key == "tp") { + t.Errorf("getRawQuery: key %s is required value.", key) + } + } + if actualValue != expectedValue { + t.Errorf("getRawQuery: %s value does not match expected %s, actual %s", key, expectedValue, actualValue) + } + } + } +} + +func TestGetSizes(t *testing.T) { + // Test items + var request *openrtb.Imp + var size string + multiFormatBanner := &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}, {W: 320, H: 50}}} + noFormatBanner := &openrtb.Banner{Format: []openrtb.Format{}} + nativeFormat := &openrtb.Native{} + + request = &openrtb.Imp{Banner: multiFormatBanner} + size = getSizes(request) + if size != "300×250,320×50" { + t.Errorf("%v does not match size.", multiFormatBanner) + } + request = &openrtb.Imp{Banner: noFormatBanner} + size = getSizes(request) + if size != "" { + t.Errorf("%v does not match size.", noFormatBanner) + } + request = &openrtb.Imp{Native: nativeFormat} + size = getSizes(request) + if size != "" { + t.Errorf("%v does not match size.", nativeFormat) + } +} + +func TestGetCurrency(t *testing.T) { + bidder := NewAdgenerationAdapter("https://d.socdm.com/adsv/v1") + // Test items + var request *openrtb.BidRequest + var currency string + innerDefaultCur := []string{"USD", "JPY"} + usdCur := []string{"USD", "EUR"} + + request = &openrtb.BidRequest{Cur: innerDefaultCur} + currency = bidder.getCurrency(request) + if currency != "JPY" { + t.Errorf("%v does not match currency.", innerDefaultCur) + } + request = &openrtb.BidRequest{Cur: usdCur} + currency = bidder.getCurrency(request) + if currency != "USD" { + t.Errorf("%v does not match currency.", usdCur) + } +} + +func TestCreateAd(t *testing.T) { + // Test items + adgBannerImpId := "test-banner-imp" + adgBannerResponse := adgServerResponse{ + Ad: "\n\n\n\n\n
\n\n
\n\n", + Beacon: "", + Beaconurl: "https://dummy-beacon.com", + Cpm: 50, + Creativeid: "DummyDsp_SdkTeam_supership.jp", + H: 300, + W: 250, + Ttl: 10, + LandingUrl: "", + Scheduleid: "111111", + } + matchBannerTag := "
\n\n
\n" + + adgVastImpId := "test-vast-imp" + adgVastResponse := adgServerResponse{ + Ad: "\n\n\n\n\n
\n\n
\n\n", + Beacon: "", + Beaconurl: "https://dummy-beacon.com", + Cpm: 50, + Creativeid: "DummyDsp_SdkTeam_supership.jp", + H: 300, + W: 250, + Ttl: 10, + LandingUrl: "", + Vastxml: "", + Scheduleid: "111111", + } + matchVastTag := "
" + + bannerAd := createAd(&adgBannerResponse, adgBannerImpId) + if bannerAd != matchBannerTag { + t.Errorf("%v does not match createAd.", adgBannerResponse) + } + vastAd := createAd(&adgVastResponse, adgVastImpId) + if vastAd != matchVastTag { + t.Errorf("%v does not match createAd.", adgVastResponse) + } +} diff --git a/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json new file mode 100644 index 00000000000..d23a510bee5 --- /dev/null +++ b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json @@ -0,0 +1,151 @@ +{ + "mockBidRequest":{ + "id": "some-request-id", + "site": { + "page": "http://example.com/test.html" + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "id": "58278" + } + } + } + ], + "tmax": 500 + }, + "httpCalls": [ + { + "internalRequest": { + "id": "some-request-id", + "site": { + "page": "http://example.com/test.html" + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "id": "58278" + } + } + } + ], + "tmax": 500 + }, + "expectedRequest":{ + "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ] + } + }, + "mockResponse":{ + "status": 200, + "body": { + "ad": "\n \n \n +` + +type ResponseAdUnit struct { + ID string `json:"id"` + CrID string `json:"crid"` + Currency string `json:"currency"` + Price string `json:"price"` + Width string `json:"width"` + Height string `json:"height"` + Code string `json:"code"` + WinURL string `json:"winUrl"` + StatsURL string `json:"statsUrl"` + Error string `json:"error"` +} + +func NewAdOceanBidder(client *http.Client, endpointTemplateString string) *AdOceanAdapter { + a := &adapters.HTTPAdapter{Client: client} + endpointTemplate, err := template.New("endpointTemplate").Parse(endpointTemplateString) + if err != nil { + glog.Fatal("Unable to parse endpoint template") + return nil + } + + whiteSpace := regexp.MustCompile(`\s+`) + + return &AdOceanAdapter{ + http: a, + endpointTemplate: *endpointTemplate, + measurementCode: whiteSpace.ReplaceAllString(measurementCode, " "), + } +} + +type AdOceanAdapter struct { + http *adapters.HTTPAdapter + endpointTemplate template.Template + measurementCode string +} + +func (a *AdOceanAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impression in the bid request", + }} + } + + consentString := "" + if request.User != nil { + var extUser openrtb_ext.ExtUser + if err := json.Unmarshal(request.User.Ext, &extUser); err == nil { + consentString = extUser.Consent + } + } + + var httpRequests []*adapters.RequestData + var errors []error + + for _, auction := range request.Imp { + newHttpRequest, err := a.makeRequest(httpRequests, &auction, request, consentString) + if err != nil { + errors = append(errors, err) + } else if newHttpRequest != nil { + httpRequests = append(httpRequests, newHttpRequest) + } + } + + return httpRequests, errors +} + +func (a *AdOceanAdapter) makeRequest(existingRequests []*adapters.RequestData, imp *openrtb.Imp, request *openrtb.BidRequest, consentString string) (*adapters.RequestData, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "Error parsing bidderExt object", + } + } + + var adOceanExt openrtb_ext.ExtImpAdOcean + if err := json.Unmarshal(bidderExt.Bidder, &adOceanExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "Error parsing adOceanExt parameters", + } + } + + addedToExistingRequest := addToExistingRequest(existingRequests, &adOceanExt, imp.ID) + if addedToExistingRequest { + return nil, nil + } + + url, err := a.makeURL(&adOceanExt, imp.ID, request, consentString) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + if request.Device != nil { + headers.Add("User-Agent", request.Device.UA) + + if request.Device.IP != "" { + headers.Add("X-Forwarded-For", request.Device.IP) + } else if request.Device.IPv6 != "" { + headers.Add("X-Forwarded-For", request.Device.IPv6) + } + } + + if request.Site != nil { + headers.Add("Referer", request.Site.Page) + } + + return &adapters.RequestData{ + Method: "GET", + Uri: url, + Headers: headers, + }, nil +} + +func addToExistingRequest(existingRequests []*adapters.RequestData, newParams *openrtb_ext.ExtImpAdOcean, auctionID string) bool { +requestsLoop: + for _, request := range existingRequests { + endpointURL, _ := url.Parse(request.Uri) + queryParams := endpointURL.Query() + masterID := queryParams["id"][0] + + if masterID == newParams.MasterID { + aids := queryParams["aid"] + for _, aid := range aids { + slaveID := strings.SplitN(aid, ":", 2)[0] + if slaveID == newParams.SlaveID { + continue requestsLoop + } + } + + queryParams.Add("aid", newParams.SlaveID+":"+auctionID) + endpointURL.RawQuery = queryParams.Encode() + newUri := endpointURL.String() + if len(newUri) < maxUriLength { + request.Uri = newUri + return true + } + } + } + + return false +} + +func (a *AdOceanAdapter) makeURL(params *openrtb_ext.ExtImpAdOcean, auctionID string, request *openrtb.BidRequest, consentString string) (string, error) { + endpointParams := macros.EndpointTemplateParams{Host: params.EmitterDomain} + host, err := macros.ResolveMacros(a.endpointTemplate, endpointParams) + if err != nil { + return "", &errortypes.BadInput{ + Message: "Unable to parse endpoint url template: " + err.Error(), + } + } + + endpointURL, err := url.Parse(host) + if err != nil { + return "", &errortypes.BadInput{ + Message: "Malformed URL: " + err.Error(), + } + } + + randomizedPart := 10000000 + rand.Intn(99999999-10000000) + if request.Test == 1 { + randomizedPart = 10000000 + } + endpointURL.Path = "/_" + strconv.Itoa(randomizedPart) + "/ad.json" + + queryParams := url.Values{} + queryParams.Add("pbsrv_v", adapterVersion) + queryParams.Add("id", params.MasterID) + queryParams.Add("nc", "1") + queryParams.Add("nosecure", "1") + queryParams.Add("aid", params.SlaveID+":"+auctionID) + if consentString != "" { + queryParams.Add("gdpr_consent", consentString) + queryParams.Add("gdpr", "1") + } + if request.User != nil && request.User.BuyerUID != "" { + queryParams.Add("hcuserid", request.User.BuyerUID) + } + endpointURL.RawQuery = queryParams.Encode() + + return endpointURL.String(), nil +} + +func (a *AdOceanAdapter) MakeBids( + internalRequest *openrtb.BidRequest, + externalRequest *adapters.RequestData, + response *adapters.ResponseData, +) (*adapters.BidderResponse, []error) { + if response.StatusCode != http.StatusOK { + return nil, []error{fmt.Errorf("Unexpected status code: %d. Network error?", response.StatusCode)} + } + + requestURL, _ := url.Parse(externalRequest.Uri) + queryParams := requestURL.Query() + auctionIDs := queryParams["aid"] + + bidResponses := make([]ResponseAdUnit, 0) + if err := json.Unmarshal(response.Body, &bidResponses); err != nil { + return nil, []error{err} + } + + var parsedResponses = adapters.NewBidderResponseWithBidsCapacity(len(auctionIDs)) + var errors []error + var slaveToAuctionIDMap = make(map[string]string, len(auctionIDs)) + + for _, auctionFullID := range auctionIDs { + auctionIDsSlice := strings.SplitN(auctionFullID, ":", 2) + slaveToAuctionIDMap[auctionIDsSlice[0]] = auctionIDsSlice[1] + } + + for _, bid := range bidResponses { + if auctionID, found := slaveToAuctionIDMap[bid.ID]; found { + if bid.Error == "true" { + continue + } + + price, _ := strconv.ParseFloat(bid.Price, 64) + width, _ := strconv.ParseUint(bid.Width, 10, 64) + height, _ := strconv.ParseUint(bid.Height, 10, 64) + adCode, err := a.prepareAdCodeForBid(bid) + if err != nil { + errors = append(errors, err) + continue + } + + parsedResponses.Bids = append(parsedResponses.Bids, &adapters.TypedBid{ + Bid: &openrtb.Bid{ + ID: bid.ID, + ImpID: auctionID, + Price: price, + AdM: adCode, + CrID: bid.CrID, + W: width, + H: height, + }, + BidType: openrtb_ext.BidTypeBanner, + }) + parsedResponses.Currency = bid.Currency + } + } + + return parsedResponses, errors +} + +func (a *AdOceanAdapter) prepareAdCodeForBid(bid ResponseAdUnit) (string, error) { + sspCode, err := url.QueryUnescape(bid.Code) + if err != nil { + return "", err + } + + adCode := fmt.Sprintf(a.measurementCode, bid.WinURL, bid.StatsURL) + sspCode + + return adCode, nil +} diff --git a/adapters/adocean/adocean_test.go b/adapters/adocean/adocean_test.go new file mode 100644 index 00000000000..5713b02da27 --- /dev/null +++ b/adapters/adocean/adocean_test.go @@ -0,0 +1,12 @@ +package adocean + +import ( + "net/http" + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "adoceantest", NewAdOceanBidder(new(http.Client), "https://{{.Host}}")) +} diff --git a/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json b/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json new file mode 100644 index 00000000000..007a530621a --- /dev/null +++ b/adapters/adocean/adoceantest/exemplary/multi-banner-impression.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b", + "source": { + "tid": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b" + }, + "tmax": 1000, + "imp": [{ + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }, { + "id": "secod-twelve", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaowafpdwlrks" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://192.168.100.203/testing/prebid_server/test.html" + }, + "device": { + "w": 418, + "h": 961 + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw" + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Asecod-twelve&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [{ + "id": "adoceanmyaozpniqismex", + "price": "1", + "winurl": "https://win-url.com", + "statsUrl": "https://stats-url.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + }, + { + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "https://win-url2.com", + "statsUrl": "https://stats-url2.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + } + ] + } + }], + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaozpniqismex", + "impid": "ao-test", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + },{ + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "secod-twelve", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/adocean/adoceantest/exemplary/single-banner-impression.json b/adapters/adocean/adoceantest/exemplary/single-banner-impression.json new file mode 100644 index 00000000000..b938a042a80 --- /dev/null +++ b/adapters/adocean/adoceantest/exemplary/single-banner-impression.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "9ed903f4-383d-406b-8011-4f06526cb02c", + "source": { + "tid": "9ed903f4-383d-406b-8011-4f06526cb02c" + }, + "tmax": 1000, + "imp": [ + { + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://example.com/test.html" + }, + "device": { + "w": 1280, + "h": 720, + "ip": "192.168.1.1" + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": "adoceanmyaozpniqismex", + "price": "1", + "winurl": "https://win-url.com", + "statsUrl": "https://stats-url.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + }, + { + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "", + "statsUrl": "", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "adoceanmyaozpniqismex", + "impid": "ao-test", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adocean/adoceantest/params/race/banner.json b/adapters/adocean/adoceantest/params/race/banner.json new file mode 100644 index 00000000000..f9f38481350 --- /dev/null +++ b/adapters/adocean/adoceantest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" +} diff --git a/adapters/adocean/adoceantest/supplemental/bad-response.json b/adapters/adocean/adoceantest/supplemental/bad-response.json new file mode 100644 index 00000000000..514262d5d2e --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/bad-response.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "9ed903f4-383d-406b-8011-4f06526cb02c", + "source": { + "tid": "9ed903f4-383d-406b-8011-4f06526cb02c" + }, + "tmax": 1000, + "imp": [ + { + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://example.com/test.html" + }, + "device": { + "w": 1280, + "h": 720, + "ip": "192.168.1.1" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": "{ key: nil }" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type []adocean.ResponseAdUnit", + "comparison": "literal" + } + ] +} diff --git a/adapters/adocean/adoceantest/supplemental/encode-error.json b/adapters/adocean/adoceantest/supplemental/encode-error.json new file mode 100644 index 00000000000..2f775f98748 --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/encode-error.json @@ -0,0 +1,80 @@ +{ + "mockBidRequest": { + "id": "9ed903f4-383d-406b-8011-4f06526cb02c", + "source": { + "tid": "9ed903f4-383d-406b-8011-4f06526cb02c" + }, + "tmax": 1000, + "imp": [ + { + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://example.com/test.html" + }, + "device": { + "w": 1280, + "h": 720, + "ip": "192.168.1.1" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": "adoceanmyaozpniqismex", + "price": "1", + "winurl": "", + "statsUrl": "", + "code": " %a", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + } + ] + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "invalid URL escape \"%a\"", + "comparison": "literal" + } + ] +} diff --git a/adapters/adocean/adoceantest/supplemental/network-error.json b/adapters/adocean/adoceantest/supplemental/network-error.json new file mode 100644 index 00000000000..7a5fa8fd18e --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/network-error.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "9ed903f4-383d-406b-8011-4f06526cb02c", + "source": { + "tid": "9ed903f4-383d-406b-8011-4f06526cb02c" + }, + "tmax": 1000, + "imp": [ + { + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://example.com/test.html" + }, + "device": { + "w": 1280, + "h": 720, + "ip": "192.168.1.1" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Network error?", + "comparison": "literal" + } + ] +} diff --git a/adapters/adocean/adoceantest/supplemental/no-bid.json b/adapters/adocean/adoceantest/supplemental/no-bid.json new file mode 100644 index 00000000000..ed81bb35114 --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/no-bid.json @@ -0,0 +1,159 @@ +{ + "mockBidRequest": { + "id": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b", + "source": { + "tid": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b" + }, + "tmax": 1000, + "imp": [{ + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }, { + "id": "ao-test-two", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaowafpdwlrks" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }, { + "id": "ao-test-three", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaowafpdwlrks" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://localhost/prebid_server/test.html" + }, + "device": { + "w": 418, + "h": 961 + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw" + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [{ + "id": "adoceanmyaozpniqismex", + "error": "true" + }, + { + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "https://win-url2.com", + "statsUrl": "https://stats-url2.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + } + ] + } + }, { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [{ + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "https://win-url3.com", + "statsUrl": "https://stats-url3.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + }] + } + }], + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-two", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }, { + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-three", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/adocean/adoceantest/supplemental/no-impression.json b/adapters/adocean/adoceantest/supplemental/no-impression.json new file mode 100644 index 00000000000..8f2a8eef351 --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/no-impression.json @@ -0,0 +1,36 @@ +{ + "mockBidRequest": { + "id": "9ed903f4-383d-406b-8011-4f06526cb02c", + "source": { + "tid": "9ed903f4-383d-406b-8011-4f06526cb02c" + }, + "tmax": 1000, + "imp": [], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://example.com/test.html" + }, + "device": { + "w": 1280, + "h": 720, + "ip": "192.168.1.1" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the bid request", + "comparison": "literal" + } + ] +} diff --git a/adapters/adocean/adoceantest/supplemental/requests-merge.json b/adapters/adocean/adoceantest/supplemental/requests-merge.json new file mode 100644 index 00000000000..9b5eb39aee2 --- /dev/null +++ b/adapters/adocean/adoceantest/supplemental/requests-merge.json @@ -0,0 +1,179 @@ +{ + "mockBidRequest": { + "id": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b", + "source": { + "tid": "b5300274-a7ec-4cdb-bf5b-d75eeb481a6b" + }, + "tmax": 1000, + "imp": [{ + "id": "ao-test", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }, { + "id": "ao-test-two", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaowafpdwlrks" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }, { + "id": "ao-test-three", + "ext": { + "bidder": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaowafpdwlrks" + } + }, + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + } + }], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": false + } + } + }, + "site": { + "publisher": { + "id": "1" + }, + "page": "http://localhost/prebid_server/test.html" + }, + "device": { + "w": 418, + "h": 961 + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw" + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [{ + "id": "adoceanmyaozpniqismex", + "price": "1", + "winurl": "https://win-url.com", + "statsUrl": "https://stats-url.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + }, + { + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "https://win-url2.com", + "statsUrl": "https://stats-url2.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + } + ] + } + }, { + "expectedRequest": { + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + }, + "mockResponse": { + "status": 200, + "body": [{ + "id": "adoceanmyaowafpdwlrks", + "price": "1", + "winurl": "https://win-url3.com", + "statsUrl": "https://stats-url3.com", + "code": " ", + "currency": "EUR", + "minFloorPrice": "0.01", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "ttl": "300" + }] + } + }], + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaozpniqismex", + "impid": "ao-test", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }, { + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-two", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }, { + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-three", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/adocean/params_test.go b/adapters/adocean/params_test.go new file mode 100644 index 00000000000..1a88c4716e0 --- /dev/null +++ b/adapters/adocean/params_test.go @@ -0,0 +1,50 @@ +package adocean + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdOcean, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adocean params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdOcean, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`, +} + +var invalidParams = []string{ + `{}`, + `{"masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`, + `{"emiter": "myao.adocean.pl", "slaveId": "adoceanmyaozpniqismex"}`, + `{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7"}`, + `{"emiter": "", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`, + `{"emiter": "myao.adocean.pl", "", "slaveId": "adoceanmyaozpniqismex"}`, + `{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": ""}`, + `{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7Z utQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmyaozpniqismex"}`, + `{"emiter": "myao.adocean.pl", "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", "slaveId": "adoceanmy iqismex"}`, +} diff --git a/adapters/adocean/usersync.go b/adapters/adocean/usersync.go new file mode 100644 index 00000000000..650e517a578 --- /dev/null +++ b/adapters/adocean/usersync.go @@ -0,0 +1,12 @@ +package adocean + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewAdOceanSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("adocean", 328, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/adocean/usersync_test.go b/adapters/adocean/usersync_test.go new file mode 100644 index 00000000000..9ca81b98cb4 --- /dev/null +++ b/adapters/adocean/usersync_test.go @@ -0,0 +1,34 @@ +package adocean + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestAdOceanSyncer(t *testing.T) { + syncURL := "https://sync-host.com/redataredir/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&url=localhost%2Fsetuid%3Fbidder%3Dadocean%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3DUUID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAdOceanSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "consent-string", + }, + }) + + assert.NoError(t, err) + assert.Equal( + t, + "https://sync-host.com/redataredir/?gdpr=1&gdpr_consent=consent-string&url=localhost%2Fsetuid%3Fbidder%3Dadocean%26gdpr%3D1%26gdpr_consent%3Dconsent-string%26uid%3DUUID", + syncInfo.URL, + ) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 328, syncer.GDPRVendorID()) +} diff --git a/config/config.go b/config/config.go index 2d49b0605a1..7e4e4196cd9 100755 --- a/config/config.go +++ b/config/config.go @@ -500,6 +500,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") + // openrtb_ext.BidderAdOcean doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAJA, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Daja%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25s") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -692,6 +693,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}") v.SetDefault("adapters.admixer.endpoint", "http://inv-nets.admixer.net/pbs.aspx") + v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 3bf1b6a5c82..e6cce7a643b 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -12,6 +12,7 @@ import ( "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" "github.com/prebid/prebid-server/adapters/admixer" + "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adoppler" "github.com/prebid/prebid-server/adapters/adpone" "github.com/prebid/prebid-server/adapters/adtelligent" @@ -84,6 +85,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdkernel: adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint), openrtb_ext.BidderAdkernelAdn: adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint), openrtb_ext.BidderAdmixer: admixer.NewAdmixerBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdmixer))].Endpoint), + openrtb_ext.BidderAdOcean: adocean.NewAdOceanBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdOcean))].Endpoint), openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), openrtb_ext.BidderAdpone: adpone.NewAdponeBidder(cfg.Adapters[string(openrtb_ext.BidderAdpone)].Endpoint), openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 6660ddac946..aa8e959f7a5 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -31,6 +31,7 @@ const ( BidderAdkernelAdn BidderName = "adkernelAdn" BidderAdpone BidderName = "adpone" BidderAdmixer BidderName = "admixer" + BidderAdOcean BidderName = "adocean" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" BidderAJA BidderName = "aja" @@ -98,6 +99,7 @@ var BidderMap = map[string]BidderName{ "adkernel": BidderAdkernel, "adkernelAdn": BidderAdkernelAdn, "admixer": BidderAdmixer, + "adocean": BidderAdOcean, "adpone": BidderAdpone, "adtelligent": BidderAdtelligent, "advangelists": BidderAdvangelists, diff --git a/openrtb_ext/imp_adocean.go b/openrtb_ext/imp_adocean.go new file mode 100644 index 00000000000..e690e929778 --- /dev/null +++ b/openrtb_ext/imp_adocean.go @@ -0,0 +1,7 @@ +package openrtb_ext + +type ExtImpAdOcean struct { + EmitterDomain string `json:"emiter"` + MasterID string `json:"masterId"` + SlaveID string `json:"slaveId"` +} diff --git a/static/bidder-info/adocean.yaml b/static/bidder-info/adocean.yaml new file mode 100644 index 00000000000..2f31fe92eaf --- /dev/null +++ b/static/bidder-info/adocean.yaml @@ -0,0 +1,6 @@ +maintainer: + email: "aoteam@gemius.com" +capabilities: + site: + mediaTypes: + - banner diff --git a/static/bidder-params/adocean.json b/static/bidder-params/adocean.json new file mode 100644 index 00000000000..7530c64784c --- /dev/null +++ b/static/bidder-params/adocean.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AdOcean Adapter Params", + "description": "A schema which validates params accepted by the AdOcean adapter", + "type": "object", + "properties": { + "emiter": { + "type": "string", + "description": "AdOcean emiter", + "pattern": ".+" + }, + "masterId": { + "type": "string", + "description": "Master's id", + "pattern": "^[\\w.]+$" + }, + "slaveId": { + "type": "string", + "description": "Slave's id", + "pattern": "^adocean[\\w.]+$" + } + }, + "required": ["emiter", "masterId", "slaveId"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 2c9cf59781b..1d8c8a0794c 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -10,6 +10,7 @@ import ( "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" "github.com/prebid/prebid-server/adapters/admixer" + "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adpone" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" @@ -77,6 +78,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernel, adkernel.NewAdkernelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernelAdn, adkernelAdn.NewAdkernelAdnSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdmixer, admixer.NewAdmixerSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAdOcean, adocean.NewAdOceanSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdpone, adpone.NewadponeSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdtelligent, adtelligent.NewAdtelligentSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 221302dd333..050e1039000 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -19,6 +19,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdkernel): syncConfig, string(openrtb_ext.BidderAdkernelAdn): syncConfig, string(openrtb_ext.BidderAdmixer): syncConfig, + string(openrtb_ext.BidderAdOcean): syncConfig, string(openrtb_ext.BidderAdpone): syncConfig, string(openrtb_ext.BidderAdtelligent): syncConfig, string(openrtb_ext.BidderAdvangelists): syncConfig, From 8db5479aecd52455facbcc9484a8bb8f128984e5 Mon Sep 17 00:00:00 2001 From: trchandraprakash <47793448+trchandraprakash@users.noreply.github.com> Date: Wed, 6 May 2020 07:29:16 -0700 Subject: [PATCH 072/318] LunaMedia Adapter (#1285) Co-authored-by: Chandra Prakash --- adapters/lunamedia/lunamedia.go | 236 ++++++++++++++++++ adapters/lunamedia/lunamedia_test.go | 10 + .../lunamediatest/exemplary/banner.json | 95 +++++++ .../lunamediatest/exemplary/video.json | 83 ++++++ .../lunamediatest/params/race/banner.json | 4 + .../lunamediatest/params/race/video.json | 4 + .../lunamediatest/supplemental/checkImp.json | 14 ++ .../lunamediatest/supplemental/compat.json | 80 ++++++ .../lunamediatest/supplemental/ext.json | 33 +++ .../supplemental/missingpub.json | 35 +++ .../supplemental/responseCode.json | 78 ++++++ .../supplemental/responsebid.json | 79 ++++++ .../lunamediatest/supplemental/site.json | 103 ++++++++ .../lunamediatest/supplemental/size.json | 28 +++ adapters/lunamedia/params_test.go | 45 ++++ adapters/lunamedia/usersync.go | 12 + adapters/lunamedia/usersync_test.go | 31 +++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_lunamedia.go | 6 + static/bidder-info/lunamedia.yaml | 13 + static/bidder-params/lunamedia.json | 18 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 25 files changed, 1016 insertions(+) create mode 100644 adapters/lunamedia/lunamedia.go create mode 100644 adapters/lunamedia/lunamedia_test.go create mode 100644 adapters/lunamedia/lunamediatest/exemplary/banner.json create mode 100644 adapters/lunamedia/lunamediatest/exemplary/video.json create mode 100644 adapters/lunamedia/lunamediatest/params/race/banner.json create mode 100644 adapters/lunamedia/lunamediatest/params/race/video.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/checkImp.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/compat.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/ext.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/missingpub.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/responseCode.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/responsebid.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/site.json create mode 100644 adapters/lunamedia/lunamediatest/supplemental/size.json create mode 100644 adapters/lunamedia/params_test.go create mode 100644 adapters/lunamedia/usersync.go create mode 100644 adapters/lunamedia/usersync_test.go create mode 100755 openrtb_ext/imp_lunamedia.go create mode 100644 static/bidder-info/lunamedia.yaml create mode 100644 static/bidder-params/lunamedia.json diff --git a/adapters/lunamedia/lunamedia.go b/adapters/lunamedia/lunamedia.go new file mode 100644 index 00000000000..51906884331 --- /dev/null +++ b/adapters/lunamedia/lunamedia.go @@ -0,0 +1,236 @@ +package lunamedia + +import ( + "encoding/json" + "fmt" + "net/http" + "text/template" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type LunaMediaAdapter struct { + EndpointTemplate template.Template +} + +//MakeRequests prepares request information for prebid-server core +func (adapter *LunaMediaAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + errs := make([]error, 0, len(request.Imp)) + if len(request.Imp) == 0 { + errs = append(errs, &errortypes.BadInput{Message: "No impression in the bid request"}) + return nil, errs + } + pub2impressions, imps, err := getImpressionsInfo(request.Imp) + if len(imps) == 0 { + return nil, err + } + errs = append(errs, err...) + + if len(pub2impressions) == 0 { + return nil, errs + } + + result := make([]*adapters.RequestData, 0, len(pub2impressions)) + for k, imps := range pub2impressions { + bidRequest, err := adapter.buildAdapterRequest(request, &k, imps) + if err != nil { + errs = append(errs, err) + return nil, errs + } else { + result = append(result, bidRequest) + } + } + return result, errs +} + +// getImpressionsInfo checks each impression for validity and returns impressions copy with corresponding exts +func getImpressionsInfo(imps []openrtb.Imp) (map[openrtb_ext.ExtImpLunaMedia][]openrtb.Imp, []openrtb.Imp, []error) { + errors := make([]error, 0, len(imps)) + resImps := make([]openrtb.Imp, 0, len(imps)) + res := make(map[openrtb_ext.ExtImpLunaMedia][]openrtb.Imp) + + for _, imp := range imps { + impExt, err := getImpressionExt(&imp) + if err != nil { + errors = append(errors, err) + continue + } + if err := validateImpression(impExt); err != nil { + errors = append(errors, err) + continue + } + //dispatchImpressions + //Group impressions by LunaMedia-specific parameters `pubid + if err := compatImpression(&imp); err != nil { + errors = append(errors, err) + continue + } + if res[*impExt] == nil { + res[*impExt] = make([]openrtb.Imp, 0) + } + res[*impExt] = append(res[*impExt], imp) + resImps = append(resImps, imp) + } + return res, resImps, errors +} + +func validateImpression(impExt *openrtb_ext.ExtImpLunaMedia) error { + if impExt.PublisherID == "" { + return &errortypes.BadInput{Message: "No pubid value provided"} + } + return nil +} + +//Alter impression info to comply with LunaMedia platform requirements +func compatImpression(imp *openrtb.Imp) error { + imp.Ext = nil //do not forward ext to LunaMedia platform + if imp.Banner != nil { + return compatBannerImpression(imp) + } + return nil +} + +func compatBannerImpression(imp *openrtb.Imp) error { + // Create a copy of the banner, since imp is a shallow copy of the original. + + bannerCopy := *imp.Banner + banner := &bannerCopy + //As banner.w/h are required fields for LunaMedia platform - take the first format entry + if banner.W == nil || banner.H == nil { + if len(banner.Format) == 0 { + return &errortypes.BadInput{Message: "Expected at least one banner.format entry or explicit w/h"} + } + format := banner.Format[0] + banner.Format = banner.Format[1:] + banner.W = &format.W + banner.H = &format.H + imp.Banner = banner + } + return nil +} + +func getImpressionExt(imp *openrtb.Imp) (*openrtb_ext.ExtImpLunaMedia, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + var LunaMediaExt openrtb_ext.ExtImpLunaMedia + if err := json.Unmarshal(bidderExt.Bidder, &LunaMediaExt); err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + return &LunaMediaExt, nil +} + +func (adapter *LunaMediaAdapter) buildAdapterRequest(prebidBidRequest *openrtb.BidRequest, params *openrtb_ext.ExtImpLunaMedia, imps []openrtb.Imp) (*adapters.RequestData, error) { + newBidRequest := createBidRequest(prebidBidRequest, params, imps) + reqJSON, err := json.Marshal(newBidRequest) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + + url, err := adapter.buildEndpointURL(params) + if err != nil { + return nil, err + } + + return &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: reqJSON, + Headers: headers}, nil +} + +func createBidRequest(prebidBidRequest *openrtb.BidRequest, params *openrtb_ext.ExtImpLunaMedia, imps []openrtb.Imp) *openrtb.BidRequest { + bidRequest := *prebidBidRequest + bidRequest.Imp = imps + for idx := range bidRequest.Imp { + imp := &bidRequest.Imp[idx] + imp.TagID = params.Placement + } + if bidRequest.Site != nil { + // Need to copy Site as Request is a shallow copy + siteCopy := *bidRequest.Site + bidRequest.Site = &siteCopy + bidRequest.Site.Publisher = nil + bidRequest.Site.Domain = "" + } + if bidRequest.App != nil { + // Need to copy App as Request is a shallow copy + appCopy := *bidRequest.App + bidRequest.App = &appCopy + bidRequest.App.Publisher = nil + } + return &bidRequest +} + +// Builds enpoint url based on adapter-specific pub settings from imp.ext +func (adapter *LunaMediaAdapter) buildEndpointURL(params *openrtb_ext.ExtImpLunaMedia) (string, error) { + endpointParams := macros.EndpointTemplateParams{PublisherID: params.PublisherID} + return macros.ResolveMacros(adapter.EndpointTemplate, endpointParams) +} + +//MakeBids translates LunaMedia bid response to prebid-server specific format +func (adapter *LunaMediaAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var msg = "" + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + if response.StatusCode != http.StatusOK { + msg = fmt.Sprintf("Unexpected http status code: %d", response.StatusCode) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + + } + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + msg = fmt.Sprintf("Bad server response: %d", err) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + } + if len(bidResp.SeatBid) != 1 { + var msg = fmt.Sprintf("Invalid SeatBids count: %d", len(bidResp.SeatBid)) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + } + + seatBid := bidResp.SeatBid[0] + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + + for i := 0; i < len(seatBid.Bid); i++ { + bid := seatBid.Bid[i] + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: getMediaTypeForImpID(bid.ImpID, internalRequest.Imp), + }) + } + return bidResponse, nil +} + +// getMediaTypeForImp figures out which media type this bid is for +func getMediaTypeForImpID(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + for _, imp := range imps { + if imp.ID == impID && imp.Video != nil { + return openrtb_ext.BidTypeVideo + } + } + return openrtb_ext.BidTypeBanner +} + +// NewLunaMediaAdapter to be called in prebid-server core to create LunaMedia adapter instance +func NewLunaMediaBidder(endpointTemplate string) adapters.Bidder { + template, err := template.New("endpointTemplate").Parse(endpointTemplate) + if err != nil { + return nil + } + return &LunaMediaAdapter{EndpointTemplate: *template} +} diff --git a/adapters/lunamedia/lunamedia_test.go b/adapters/lunamedia/lunamedia_test.go new file mode 100644 index 00000000000..924e6a774b1 --- /dev/null +++ b/adapters/lunamedia/lunamedia_test.go @@ -0,0 +1,10 @@ +package lunamedia + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "lunamediatest", NewLunaMediaBidder("http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}")) +} diff --git a/adapters/lunamedia/lunamediatest/exemplary/banner.json b/adapters/lunamedia/lunamediatest/exemplary/banner.json new file mode 100644 index 00000000000..3b5c417f169 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/exemplary/banner.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "bidder": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=19f1b372c7548ec1fe734d2c9f8dc688", + "body":{ + "id": "testid", + "imp": [{ + "id": "testimpid", + "tagid": "dummyplacement", + "banner": { + "format": [{ + "w": 320, + "h": 250 + }, { + "w": 320, + "h": 300 + }], + "w": 320, + "h": 250 + } + + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/lunamedia/lunamediatest/exemplary/video.json b/adapters/lunamedia/lunamediatest/exemplary/video.json new file mode 100644 index 00000000000..82217373e2e --- /dev/null +++ b/adapters/lunamedia/lunamediatest/exemplary/video.json @@ -0,0 +1,83 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=19f1b372c7548ec1fe734d2c9f8dc688", + "body":{ + "id": "testid", + "imp": [{ + "id": "testimpid", + "tagid": "dummyplacement", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/params/race/banner.json b/adapters/lunamedia/lunamediatest/params/race/banner.json new file mode 100644 index 00000000000..2eed8f2ec4e --- /dev/null +++ b/adapters/lunamedia/lunamediatest/params/race/banner.json @@ -0,0 +1,4 @@ +{ + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/params/race/video.json b/adapters/lunamedia/lunamediatest/params/race/video.json new file mode 100644 index 00000000000..2eed8f2ec4e --- /dev/null +++ b/adapters/lunamedia/lunamediatest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/checkImp.json b/adapters/lunamedia/lunamediatest/supplemental/checkImp.json new file mode 100644 index 00000000000..ca48812b4df --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/checkImp.json @@ -0,0 +1,14 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test", + "domain": "test.com" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the bid request", + "comparison": "literal" + }] + } \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/compat.json b/adapters/lunamedia/lunamediatest/supplemental/compat.json new file mode 100644 index 00000000000..5b84d3a5a39 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/compat.json @@ -0,0 +1,80 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [{ + "w": 320, + "h": 250 + }] + }, + "ext": { + "bidder": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=19f1b372c7548ec1fe734d2c9f8dc688", + "body":{ + "id": "testid", + "imp": [{ + "banner": { + "h": 250, + "w": 320 + }, + "id": "testimpid", + "tagid": "dummyplacement" + + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/lunamedia/lunamediatest/supplemental/ext.json b/adapters/lunamedia/lunamediatest/supplemental/ext.json new file mode 100644 index 00000000000..3cfb878bd47 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/ext.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + ] + }, +"expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/missingpub.json b/adapters/lunamedia/lunamediatest/supplemental/missingpub.json new file mode 100644 index 00000000000..b088917afa3 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/missingpub.json @@ -0,0 +1,35 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "bidder": { + "pubid": "", + "placement": "dummyplacement" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No pubid value provided", + "comparison": "literal" + }] + } \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/responseCode.json b/adapters/lunamedia/lunamediatest/supplemental/responseCode.json new file mode 100644 index 00000000000..739af044b29 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/responseCode.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "bidder": { + "pubid": "yu", + "placement": "dummyplacement" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=yu", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "h": 250, + "w": 320 + }, + { + "h": 300, + "w": 320 + } + ], + "h": 250, + "w": 320 + }, + "id": "testimpid", + "tagid": "dummyplacement" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "body": { + "seatbid": [] + } + } + } + + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected http status code: 0", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/responsebid.json b/adapters/lunamedia/lunamediatest/supplemental/responsebid.json new file mode 100644 index 00000000000..e9d8c3c543d --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/responsebid.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "bidder": { + "pubid": "yu", + "placement": "dummyplacement" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=yu", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "h": 250, + "w": 320 + }, + { + "h": 300, + "w": 320 + } + ], + "h": 250, + "w": 320 + }, + "id": "testimpid", + "tagid": "dummyplacement" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "status":200, + "body": { + "seatbid": [] + } + } + } + + ], + "expectedMakeBidsErrors": [ + { + "value": "Invalid SeatBids count: 0", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/site.json b/adapters/lunamedia/lunamediatest/supplemental/site.json new file mode 100644 index 00000000000..81d71554f38 --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/site.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 320, + "h": 250 + }, + { + "w": 320, + "h": 300 + } + ], + "w": 320, + "h": 250 + }, + "ext": { + "bidder": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://api.lunamedia.io/xp/get?pubid=19f1b372c7548ec1fe734d2c9f8dc688", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "h": 250, + "w": 320 + }, + { + "h": 300, + "w": 320 + } + ], + "h": 250, + "w": 320 + }, + "id": "testimpid", + "tagid": "dummyplacement" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/lunamedia/lunamediatest/supplemental/size.json b/adapters/lunamedia/lunamediatest/supplemental/size.json new file mode 100644 index 00000000000..77228559eee --- /dev/null +++ b/adapters/lunamedia/lunamediatest/supplemental/size.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test", + "domain": "test.com" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + + }, + "ext": { + "bidder": { + "pubid": "19f1b372c7548ec1fe734d2c9f8dc688", + "placement": "dummyplacement" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Expected at least one banner.format entry or explicit w/h", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/lunamedia/params_test.go b/adapters/lunamedia/params_test.go new file mode 100644 index 00000000000..b4faeea1f77 --- /dev/null +++ b/adapters/lunamedia/params_test.go @@ -0,0 +1,45 @@ +package lunamedia + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderLunaMedia, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected LunaMedia params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderLunaMedia, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"pubid": "19f1b372c7548ec1fe734d2c9f8dc688"}`, +} + +var invalidParams = []string{ + `{"publisher": "19f1b372c7548ec1fe734d2c9f8dc688"}`, + `nil`, + ``, + `[]`, + `true`, +} diff --git a/adapters/lunamedia/usersync.go b/adapters/lunamedia/usersync.go new file mode 100644 index 00000000000..7ad54e384a1 --- /dev/null +++ b/adapters/lunamedia/usersync.go @@ -0,0 +1,12 @@ +package lunamedia + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewLunaMediaSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("lunamedia", 0, temp, adapters.SyncTypeIframe) +} diff --git a/adapters/lunamedia/usersync_test.go b/adapters/lunamedia/usersync_test.go new file mode 100644 index 00000000000..c9fe2032d2c --- /dev/null +++ b/adapters/lunamedia/usersync_test.go @@ -0,0 +1,31 @@ +package lunamedia + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestLunaMediaSyncer(t *testing.T) { + syncURL := "https://api.lunamedia.io/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=lunamedia&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&uid=$UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewLunaMediaSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "A", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://api.lunamedia.io/xp/user-sync?acctid={aid}&&redirect=localhost/setuid?bidder=lunamedia&gdpr=1&gdpr_consent=A&uid=$UID", syncInfo.URL) + assert.Equal(t, "iframe", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 7e4e4196cd9..a7132edbc81 100755 --- a/config/config.go +++ b/config/config.go @@ -522,6 +522,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLunaMedia, "https://api.lunamedia.io/xp/user-sync?redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlunamedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderNanoInteractive, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -722,6 +723,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid") v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest") v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") + v.SetDefault("adapters.lunamedia.endpoint", "http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet") v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/") v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index e6cce7a643b..17814b3639a 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -40,6 +40,7 @@ import ( "github.com/prebid/prebid-server/adapters/kubient" "github.com/prebid/prebid-server/adapters/lifestreet" "github.com/prebid/prebid-server/adapters/lockerdome" + "github.com/prebid/prebid-server/adapters/lunamedia" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" "github.com/prebid/prebid-server/adapters/nanointeractive" @@ -113,6 +114,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint), openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), + openrtb_ext.BidderLunaMedia: lunamedia.NewLunaMediaBidder(cfg.Adapters[string(openrtb_ext.BidderLunaMedia)].Endpoint), openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index aa8e959f7a5..c9b7f7a0519 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -58,6 +58,7 @@ const ( BidderKubient BidderName = "kubient" BidderLifestreet BidderName = "lifestreet" BidderLockerDome BidderName = "lockerdome" + BidderLunaMedia BidderName = "lunamedia" BidderMarsmedia BidderName = "marsmedia" BidderMgid BidderName = "mgid" BidderNanoInteractive BidderName = "nanointeractive" @@ -127,6 +128,7 @@ var BidderMap = map[string]BidderName{ "kubient": BidderKubient, "lifestreet": BidderLifestreet, "lockerdome": BidderLockerDome, + "lunamedia": BidderLunaMedia, "marsmedia": BidderMarsmedia, "mgid": BidderMgid, "nanointeractive": BidderNanoInteractive, diff --git a/openrtb_ext/imp_lunamedia.go b/openrtb_ext/imp_lunamedia.go new file mode 100755 index 00000000000..e7e4dd6593c --- /dev/null +++ b/openrtb_ext/imp_lunamedia.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtImpLunaMedia struct { + PublisherID string `json:"pubid"` + Placement string `json:"placement,omitempty"` +} diff --git a/static/bidder-info/lunamedia.yaml b/static/bidder-info/lunamedia.yaml new file mode 100644 index 00000000000..4cabdc4a381 --- /dev/null +++ b/static/bidder-info/lunamedia.yaml @@ -0,0 +1,13 @@ +maintainer: + email: "josh@lunamedia.io" +capabilities: + site: + mediaTypes: + - banner + - video + + app: + mediaTypes: + - banner + - video + diff --git a/static/bidder-params/lunamedia.json b/static/bidder-params/lunamedia.json new file mode 100644 index 00000000000..1aa18cee6b9 --- /dev/null +++ b/static/bidder-params/lunamedia.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "LunaMedia Adapter Params", + "description": "A schema which validates params accepted by the LunaMedia adapter", + "type": "object", + "properties": { + "pubid": { + "type": "string", + "description": "An id used to identify LunaMedia publisher.", + "minLength": 8 + }, + "placement": { + "type": "string", + "description": "A placement created on adserver." + } + }, + "required": ["pubid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 1d8c8a0794c..42c93d652b3 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -34,6 +34,7 @@ import ( "github.com/prebid/prebid-server/adapters/ix" "github.com/prebid/prebid-server/adapters/lifestreet" "github.com/prebid/prebid-server/adapters/lockerdome" + "github.com/prebid/prebid-server/adapters/lunamedia" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" "github.com/prebid/prebid-server/adapters/nanointeractive" @@ -102,6 +103,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderLunaMedia, lunamedia.NewLunaMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMarsmedia, marsmedia.NewMarsmediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderNanoInteractive, nanointeractive.NewNanoInteractiveSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 050e1039000..44ff15bd5fe 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -43,6 +43,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderIx): syncConfig, string(openrtb_ext.BidderLifestreet): syncConfig, string(openrtb_ext.BidderLockerDome): syncConfig, + string(openrtb_ext.BidderLunaMedia): syncConfig, string(openrtb_ext.BidderMarsmedia): syncConfig, string(openrtb_ext.BidderMgid): syncConfig, string(openrtb_ext.BidderNanoInteractive): syncConfig, From 42d52814780de6cecadcdca84aaefd1f594688b7 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 6 May 2020 08:38:11 -0700 Subject: [PATCH 073/318] [Sharethrough] Add CCPA support (#1263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Handle gzip responses from ad server correctly * Bump to version 8 * [Go Modules] Add proxy (#1079) * Add SSL cert for accessing stored request API (#1087) * [misspell] fix a misspell (#1102) * update static bidder params for rubicon video to follow the json marshalling names (#1100) * Switching yieldmo auction endpoint from http to https (#1103) * Add Datablocks Adapter (#1095) * datablocks bid adapter * ttx * add test json * add coverage * redo ttx * formatted * better error handling * additional tests and recomended fixes * Adding translatecategories flag to includebrandcategory (#1098) * Making IAB category translation optional with translatecategories boolean in request * Updating exchange unit tests to remove extra bids * Updates from code review comments * Removed comment about default TranslateCategories value * Changed translateCat to translateCategories in tests * Combined helper functions in exchange_test related to TranslateCategories * Bid floor (#1085) * Currency handling fix (#1097) * facebook adapter refactor (#1064) * Kubient adapter (#1094) * [synacormedia] Update user sync url to be https (#1115) This detail was missed while setting up the adapter, but we would like to use https for the user sync. * Remove Go 1.11 Build Target (#1109) * Set "Secure" on Same SIte cookies (#1119) * TripleliftNative Adapter (#1114) * ignore swp files * start small * start really small * add a user sync * justify * triplelift adapter * add our endpoint * fix syntax * config stuff * compiler fixes * more config * add params * making progress * make our ext more exty * start making responses * more logic * fix compilation errors * can we just nil this out? * augment our json * radically simplify our json * fix errs * infer the bid type * fix syntax * fix comilation errors * rename * fix compilation error * config stuff * simplify params * more config stuff * fixes * revert this * fix up the extension * getting closer * add a test * update config * update bidder params * add the floor here, too * add a usersync test * validation, ws, and a test * update tests * fix test * update email * why not * change email * preprocess requests * do some parsing * take care of some errors * floor is optional * ws * remove native * everything is either banner or video * this should be a float * floor to floor * fix compilation errors * add some tests * more tests * more tests * simplify * more progress * format * ws * rm * don't need this * fix test * fix test * don't ignore swap * change line back * report an error if there are no valid impressions for triplelift * check for either a Banner or Video object on the impression * more tests * mv * more tests * update triplelift end point * send native * ws * start changing tests * fix more tests * update config * add redirect to triplelift usersync * fix supplier id in triplelift_test * update tl usersync endpoint and test * fix tl supplier id in test json * update usersync test template * adjust inconsistency with test and sync url * mv * update packages * mv * mv * update * fix compilation errors * rename * rename some stuff * rename * rename * fix some compilation errors * ws * ws * add the extra info * add some extra info * add some files back * ws and such * updates * ws * fix compilation error * mv * rename * Revert "rename" This reverts commit 1b77c72e1eeee580148540fbdd880e70bf699709. * Revert "mv" This reverts commit 52a134ddfaf531fe6235e4751935d4266a36e78f. * it builds * cp a file * cp another file * fix a test * fix test * add the extra info * ws * add some logic * edit comment * it compiles * this is now public * call this * add the function * return nil * seems to be working * ws * seems to be working * ws * mv * starting to work * ws * add a new function * ws * fix tests * bug fix * update some stuff * revert * take out prints * fix up diff * fix up diff * update ws * fix * ws * omit the triplelift endppint * Revert "omit the triplelift endppint" This reverts commit 7abc3e46f0fbba39041da6fff7bb2335adc1fece. * populate the endpoint through the extinfo * ws * set disabled to be default * ws * update types * fixing tests * making progres * fix tests * fix tests * more fixes for tests * fixed tests * just use a comment * get rid of endpoint * restore endpoint * add some errors around unmarshalling * ws * ws * use the literal * ws * ws * update json * simplify * ws * restore tests * fail fast when grabbing invcode * use the right type * use a different error type * bump code coverage * add a new test * change error type * ws * break out test into its own function * JSON block that has a full data-center specific URL cache info (#1104) * Update Dockerfile and Makefile (#1099) * Add option for running tests as part of the docker image building * Update Makefile - Add ability to execute adapter specific tests - Execute targets for "all" rather than just printing the target name and usage - Remove use of non-existing "install" target from .PHONY targets - Remove "build" as a dependency for "image" * enable app requests for audience network (#1122) * [docs] fix markdown title (#1124) * Prometheus Refactor (#1108) * update default sync url (#1127) * Update sync url for BidderGrid adapter (#1120) * [SonarCloud] Legacy auction endpoint (#1017) * [currency converter] allow to deduce reverse rate (#1126) This CL allows the currency rate currency to deduce a currency rate even if not directly defined in the table but the reverse rate is present. E.q. USD => EUR is 1.0897 EUR => USD is not set Old behavior when asking rate from EUR to USD will not be found, New behavior is using the known reverse rate to deduce the rate. Rate for 2 USD will be 2 * (1 / 1.0897) * Updated handleError arguments to be pointers for video endpoint (#1128) * Updated handleError arguments to be pointers for video endpoint * Removing unneeded pointer to http.ResponseWriter * Adding units test for update to handleError * Revert changes to GetExtCacheData() made in #1104 (#1130) (#1131) * Better native request validation (#1132) * require the caller to define native assets[...].ID (#1123) * require the caller to define native assets[...].ID * Update assets-with-partial-ids.json * CCPA Phase 1: AMP Endpoint (#1125) * facebook: removed Auth-Token from header (replaced by authentication_id in the request body) (#1113) * Setuid Fix (#1121) * Update http refresh to use url builder. Fixes #1065 (#1133) * Add mapping of user.ext.eids[] for LiveIntent in Rubicon bidder (#1089) * support facebook app_secret config param (#1139) * CCPA Phase 1: Cookie Sync (#1135) * null check banner.h (#1142) * Add Pubnative Adapter (#1134) * Adding the passing of CCPA value to the bid request for video endpoint (#1143) * first draft (#1137) * CCPA Phase 2: Enforcement (#1138) * Gamoshi Adapter: Update cookie sync (#1146) * Simplify static/bidder-params/triplelift_native.json (#1152) * Added US Privacy support in TheMediaGrid server adapter (#1147) * Add TheMediaGrid server adapter * Add video support in TheMediaGrid s2s adapter * Update sync url for TheMediaGrid s2s adapter * Added CCPA support for TheMediaGrid s2s adapter * Fix sync url for TheMediaGrid adapter * CCPA User Sync Updates (#1153) * Marsmedia - add new bidder (#1118) * Add Applogy adapter (#1151) * enforce video.size_id for video imps in rubicon adapter (#1101) * Updated PubMatic endpoint to use https (#1155) * Update Example AppNexus Placement ID (#1160) * Fix Currency Converter Doesn't Output CUR (#1154) * Add custom JSON req/resp data to the analytics logging… (#1145) * Add custom JSON req/resp data to the analytics logging for the /openrtb2/video endpoint. * Add calls in unit tests to cover logging and jsonify of video object. * CCPA User Sync URL Updates (#1157) * Fixes audienceNetwork adapter ignoring banner.format sizes. (#1164) * adding yieldmo vendor id to usersync (#1166) * Add SmartRTB adapter (#1071) * Added new adapter for CPMStar ad network banners and video (#1159) * Update the Conversant sync pixel (#1161) * Add imp.ext.is_rewarded_inventory flag for rewarded video in Rubicon (#1170) * [currencies] fix GetInfo() null ref issue (#1169) This CL fixes the null ref on `RateConverter.GetInfo()` when rates are nil. Issue: #1136 * Fix triplelift User Sync (#1173) * Enhance Message For Cache Errors (#1175) * Fix PubMatic Usersync URL (#1178) Co-authored-by: pm-isha-bharti * [Synacormedia] Add tagId bidder parameter (#1165) * Remove all non-secure calls from eplanning adapter (#1179) * Expose Cache HTTP Settings (#1184) * Adding bid rejection messages to debug response (#1181) * Adds timeout notifications for Facebook (#1182) * VIS.X: added app type support (#1194) * Add Adoppler bidder support. (#1186) * Add Adoppler bidder support. * Address code review comments. Use JSON-templates for testing. * Fix misprint; Add url.PathEscape call for adunit URL parameter. * Adding support for deal prefixes (#1183) * updating default hard-coded list of certs (#1201) Co-authored-by: Shalmali Patil * add admixer adapter (#1195) * Adding copying of gdpr consent string to openrtb bid request (#1189) * Adding copying of gdpr consent string to openrtb bid request * Updated video request to use OpenRTB Video and User objects * Fixing unit test failure message * Updates from code review comments * Updating unit test initialization * Updated mimes array construction * fix conversant sync pixel (#1208) * openx adapter: forward bid response currency in openx adapter if set (#1211) it was always set to the default USD before * add ucfunnel adapter (#1192) * Update required params for TheMediaGrid adapter (#1188) * add zeroclickfraud adapter (#1207) * add zeroclickfraud adapter * fixes for PR * fix casing of Zeroclickfraud * Fix Adform's parameters regex (#1214) * 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 * Create readme file for adform * Fix Adform's parameters regex Motivation: catastrophic backtracking during regex execution Details: - https://regex101.com/r/NNQrWq/1 - string to check "url_domain:keskustelu.suomi24.fi,url_path:/matkailu/matkakohteet/aasia,layout:lg,categories:Matkailu,main_category:Matkailu" Co-authored-by: v.statkevich Co-authored-by: Olga Linkevich * If Device.UA is not present in request body, init it with user-agent from header (#1219) * If Device.UA is not present in request body, init it with user-agent from request header if it's present * Moved User-Agent handler to parseVideoRequest func and added unit test * Minor clean up Co-authored-by: Veronika Solovei * Queued request timeout (#1217) Co-authored-by: Veronika Solovei * docs: adding currency support section (#1199) * Add ValueImpression Adapter (#1204) * Kidoz adapter (#1210) Co-authored-by: Ryan Haksi * Update auction.md (#1224) Fix type * Update auction.md (#1225) Fix typo. * Added logging to cache for video endpoint (#1220) * WIP added logging to cache for video endpoint * Updating cache call to use TTL from config * Updates from initial feedback * Log now includes HTTP headers * Fixed caching to use a new cache entry rather than appending to the VAST * Added feature where is query is set, the test flag is set in the request * Updated recorded response and handleError * Updates from code review comments * Changed recorded output to be only the debug ext * Removed extra marhal calls * Changed cache to be an endpoint dependency * Added debugLog struct to hold all debug related info * Numerous smaller changes * Further code cleanup and added unit tests for debug changes * Added missing error checks * Added unit test for error case * added VISX vendor ID for usersyncing (#1229) Co-authored-by: Aadesh Patel * First pass at phase 1 TCF 2.0 support (#1228) * First pass at phase 1 TCF 2.0 support * minor fixes * Update go-gdpr library and fix stuff * Fixes for PR comments * Updated price granularity unmarshal to accept empty values and ranges (#1230) * Update vendorID for TheMediaGrid s2s Bid Adapter (#1232) * treat 204 from FAN as a no bids response (#1233) Co-authored-by: Aadesh Patel * AMP CCPA Fix (#1187) * Update rubicon.md (#1234) * adding schain interface (#1203) * added Rewarded Video section (#1200) also edited all examples so they include the full openRTB context * nanointeractive adapter (#1213) * nanointeractive adapter * nanointeractive adapter, changes after review * nanointeractive adapter * nanointeractive adapter, changes after review * formatting * Typos Fix (#1236) * Fix Typo * Fixed More Typos * Moved hb_pc_cat_dur modification to be before caching (#1250) * Handle CCPA + enable gzip response [#169984259] * Addressing review (#273) [#169984259] * Remove custom gzip logic (#280) * Getting rid of custom gzip logic [#169984259] * Restore prod ad server url [#169984259] Co-authored-by: Benjamin Co-authored-by: guscarreon Co-authored-by: Aadesh Co-authored-by: Winston-Yieldmo <46379634+Winston-Yieldmo@users.noreply.github.com> Co-authored-by: htang555 Co-authored-by: Cameron Rice <37162584+camrice@users.noreply.github.com> Co-authored-by: ah-tappx <46002207+ah-tappx@users.noreply.github.com> Co-authored-by: hhhjort <31041505+hhhjort@users.noreply.github.com> Co-authored-by: Marsel Co-authored-by: Corey Kress Co-authored-by: Scott Kay Co-authored-by: Kevin Kerr Co-authored-by: Mansi Nahar Co-authored-by: Benjamin Co-authored-by: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Co-authored-by: Austin Bischoff Co-authored-by: rpanchyk Co-authored-by: Florian Hartwig Co-authored-by: Salomon Rada Co-authored-by: vladi-mmg Co-authored-by: Aleksei Lin Co-authored-by: PubMatic-OpenWrap Co-authored-by: jmaynardxandr <46759873+jmaynardxandr@users.noreply.github.com> Co-authored-by: evanmsmrtb Co-authored-by: CPMStar Co-authored-by: johnwier <49074029+johnwier@users.noreply.github.com> Co-authored-by: pm-isha-bharti Co-authored-by: Seba Perez Co-authored-by: Michael Kuryshev Co-authored-by: Viacheslav Chimishuk Co-authored-by: Shalmali Patil Co-authored-by: DmitryStashkevich <34479135+DmitryStashkevich@users.noreply.github.com> Co-authored-by: vstatkevich Co-authored-by: v.statkevich Co-authored-by: Olga Linkevich Co-authored-by: Veronika Solovei Co-authored-by: Veronika Solovei Co-authored-by: bretg Co-authored-by: thuyhq <61451682+thuyhq@users.noreply.github.com> Co-authored-by: rhaksi-kidoz <61601767+rhaksi-kidoz@users.noreply.github.com> Co-authored-by: Ryan Haksi Co-authored-by: ACannuniRP <57228257+ACannuniRP@users.noreply.github.com> Co-authored-by: Aadesh Patel Co-authored-by: Rade Popovic <32302052+nanointeractive@users.noreply.github.com> --- adapters/sharethrough/butler.go | 11 +++++++++++ adapters/sharethrough/butler_test.go | 2 ++ adapters/sharethrough/sharethrough.go | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 61081aaa3ff..522bbc4967e 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -7,6 +7,7 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/privacy/ccpa" "net/http" "net/url" "regexp" @@ -21,6 +22,7 @@ type StrAdSeverParams struct { BidID string ConsentRequired bool ConsentString string + USPrivacySignal string InstantPlayCapable bool Iframe bool Height uint64 @@ -94,6 +96,11 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr return nil, err } + usPolicySignal := "" + if usPolicy, err := ccpa.ReadPolicy(request); err == nil { + usPolicySignal = usPolicy.Value + } + return &adapters.RequestData{ Method: "POST", Uri: s.UriHelper.buildUri(StrAdSeverParams{ @@ -101,6 +108,7 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr BidID: imp.ID, ConsentRequired: s.Util.gdprApplies(request), ConsentString: userInfo.Consent, + USPrivacySignal: usPolicySignal, Iframe: strImpParams.Iframe, Height: height, Width: width, @@ -184,6 +192,9 @@ func (h StrUriHelper) buildUri(params StrAdSeverParams) string { v.Set("bidId", params.BidID) v.Set("consent_required", fmt.Sprintf("%t", params.ConsentRequired)) v.Set("consent_string", params.ConsentString) + if params.USPrivacySignal != "" { + v.Set("us_privacy", params.USPrivacySignal) + } if params.TheTradeDeskUserId != "" { v.Set("ttduid", params.TheTradeDeskUserId) } diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go index 40c59b50442..402e8365dd0 100644 --- a/adapters/sharethrough/butler_test.go +++ b/adapters/sharethrough/butler_test.go @@ -437,6 +437,7 @@ func TestBuildUri(t *testing.T) { BidID: "bid", ConsentRequired: true, ConsentString: "consent", + USPrivacySignal: "ccpa", InstantPlayCapable: true, Iframe: false, Height: 20, @@ -450,6 +451,7 @@ func TestBuildUri(t *testing.T) { "bidId=bid", "consent_required=true", "consent_string=consent", + "us_privacy=ccpa", "instant_play_capable=true", "stayInIframe=false", "height=20", diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index d1b2408ce66..5e0377ab27a 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -10,7 +10,7 @@ import ( ) const supplyId = "FGMrCMMc" -const strVersion = 7 +const strVersion = 8 func NewSharethroughBidder(endpoint string) *SharethroughAdapter { return &SharethroughAdapter{ From cd909915eeee8b1796243c4f5d1a8d5f46a6737b Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 6 May 2020 11:46:03 -0400 Subject: [PATCH 074/318] Remove Outdated GDPR AMP Special Case (#1283) --- exchange/utils.go | 3 +-- privacy/enforcement.go | 17 ++++--------- privacy/enforcement_test.go | 51 ++----------------------------------- 3 files changed, 8 insertions(+), 63 deletions(-) diff --git a/exchange/utils.go b/exchange/utils.go index d1c95b88b86..f09b11513f1 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -43,7 +43,6 @@ func cleanOpenRTBRequests(ctx context.Context, gdpr := extractGDPR(orig, usersyncIfAmbiguous) consent := extractConsent(orig) - isAMP := labels.RType == pbsmetrics.ReqTypeAMP privacyEnforcement := privacy.Enforcement{ COPPA: orig.Regs != nil && orig.Regs.COPPA == 1, @@ -66,7 +65,7 @@ func cleanOpenRTBRequests(ctx context.Context, privacyEnforcement.GDPR = false } - privacyEnforcement.Apply(bidReq, isAMP) + privacyEnforcement.Apply(bidReq) } return diff --git a/privacy/enforcement.go b/privacy/enforcement.go index caea396c0f6..592b6fb6937 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -17,14 +17,14 @@ func (e Enforcement) Any() bool { } // Apply cleans personally identifiable information from an OpenRTB bid request. -func (e Enforcement) Apply(bidRequest *openrtb.BidRequest, isAMP bool) { - e.apply(bidRequest, isAMP, NewScrubber()) +func (e Enforcement) Apply(bidRequest *openrtb.BidRequest) { + e.apply(bidRequest, NewScrubber()) } -func (e Enforcement) apply(bidRequest *openrtb.BidRequest, isAMP bool, scrubber Scrubber) { +func (e Enforcement) apply(bidRequest *openrtb.BidRequest, scrubber Scrubber) { if bidRequest != nil && e.Any() { bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceMacAndIFA(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) - bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(isAMP), e.getGeoScrubStrategy()) + bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy()) } } @@ -56,18 +56,11 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { return ScrubStrategyGeoNone } -func (e Enforcement) getUserScrubStrategy(isAMP bool) ScrubStrategyUser { +func (e Enforcement) getUserScrubStrategy() ScrubStrategyUser { if e.COPPA { return ScrubStrategyUserFull } - // There's no way for AMP to send a GDPR consent string yet so it's hard - // to know if the vendor is consented or not and therefore for AMP requests - // we keep the BuyerUID as is for GDPR. - if e.GDPR && isAMP { - return ScrubStrategyUserNone - } - if e.GDPR || e.CCPA { return ScrubStrategyUserBuyerIDOnly } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index ffc9da5d30c..3bc716b38d2 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -52,7 +52,6 @@ func TestAny(t *testing.T) { func TestApply(t *testing.T) { testCases := []struct { enforcement Enforcement - isAMP bool expectedDeviceMacAndIFA bool expectedDeviceIPv6 ScrubStrategyIPV6 expectedDeviceGeo ScrubStrategyGeo @@ -66,7 +65,6 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: true, }, - isAMP: true, expectedDeviceMacAndIFA: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, @@ -80,7 +78,6 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: false, }, - isAMP: false, expectedDeviceMacAndIFA: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, @@ -94,7 +91,6 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, }, - isAMP: false, expectedDeviceMacAndIFA: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, @@ -102,27 +98,12 @@ func TestApply(t *testing.T) { expectedUserGeo: ScrubStrategyGeoReducedPrecision, description: "GDPR", }, - { - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPR: true, - }, - isAMP: true, - expectedDeviceMacAndIFA: false, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserNone, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - description: "GDPR For AMP", - }, { enforcement: Enforcement{ CCPA: true, COPPA: false, GDPR: false, }, - isAMP: false, expectedDeviceMacAndIFA: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, @@ -130,34 +111,6 @@ func TestApply(t *testing.T) { expectedUserGeo: ScrubStrategyGeoReducedPrecision, description: "CCPA", }, - { - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: false, - }, - isAMP: true, - expectedDeviceMacAndIFA: false, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserBuyerIDOnly, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - description: "CCPA For AMP", - }, - { - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: true, - }, - isAMP: true, - expectedDeviceMacAndIFA: false, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserNone, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - description: "GDPR And CCPA For AMP", - }, } for _, test := range testCases { @@ -172,7 +125,7 @@ func TestApply(t *testing.T) { m.On("ScrubDevice", req.Device, test.expectedDeviceMacAndIFA, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(device).Once() m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(user).Once() - test.enforcement.apply(req, test.isAMP, m) + test.enforcement.apply(req, m) m.AssertExpectations(t) assert.Equal(t, device, req.Device, "Device Set Correctly") @@ -191,7 +144,7 @@ func TestApplyNoneApplicable(t *testing.T) { m := &mockScrubber{} - enforcement.apply(req, true, m) + enforcement.apply(req, m) m.AssertNotCalled(t, "ScrubDevice") m.AssertNotCalled(t, "ScrubUser") From cc3d2daa4a3a71161b153c912ebc621e3f5c0c17 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 6 May 2020 14:59:32 -0400 Subject: [PATCH 075/318] Stricter Privacy Scrubbing (#1286) * Stricter Privacy Scrubbing * Update Unit Test Style * Fixed Whitespace --- go.mod | 4 +- go.sum | 4 + privacy/enforcement.go | 18 +-- privacy/enforcement_test.go | 106 ++++++++--------- privacy/scrubber.go | 51 +++------ privacy/scrubber_test.go | 220 ++++++++++++++++++++++++++---------- 6 files changed, 244 insertions(+), 159 deletions(-) diff --git a/go.mod b/go.mod index 387b8b9815c..89cc69e4519 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect github.com/spf13/pflag v1.0.2 // indirect github.com/spf13/viper v1.1.0 - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.5.1 github.com/valyala/fasthttp v1.9.0 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect @@ -70,5 +70,5 @@ require ( golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/text v0.3.0 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - gopkg.in/yaml.v2 v2.2.1 + gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index ad9caf5004b..f929408f0f3 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw= @@ -196,3 +198,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/privacy/enforcement.go b/privacy/enforcement.go index 592b6fb6937..0230ca6b9af 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -23,15 +23,11 @@ func (e Enforcement) Apply(bidRequest *openrtb.BidRequest) { func (e Enforcement) apply(bidRequest *openrtb.BidRequest, scrubber Scrubber) { if bidRequest != nil && e.Any() { - bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceMacAndIFA(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) - bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy()) + bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) + bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getDemographicScrubStrategy(), e.getGeoScrubStrategy()) } } -func (e Enforcement) getDeviceMacAndIFA() bool { - return e.COPPA -} - func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 { if e.COPPA { return ScrubStrategyIPV6Lowest32 @@ -56,14 +52,10 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { return ScrubStrategyGeoNone } -func (e Enforcement) getUserScrubStrategy() ScrubStrategyUser { +func (e Enforcement) getDemographicScrubStrategy() ScrubStrategyDemographic { if e.COPPA { - return ScrubStrategyUserFull - } - - if e.GDPR || e.CCPA { - return ScrubStrategyUserBuyerIDOnly + return ScrubStrategyDemographicAgeAndGender } - return ScrubStrategyUserNone + return ScrubStrategyDemographicNone } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index 3bc716b38d2..c7433f8b271 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -15,31 +15,31 @@ func TestAny(t *testing.T) { description string }{ { + description: "All False", enforcement: Enforcement{ CCPA: false, COPPA: false, GDPR: false, }, - expected: false, - description: "All False", + expected: false, }, { + description: "All True", enforcement: Enforcement{ CCPA: true, COPPA: true, GDPR: true, }, - expected: true, - description: "All True", + expected: true, }, { + description: "Mixed", enforcement: Enforcement{ CCPA: false, COPPA: true, GDPR: false, }, - expected: true, - description: "Mixed", + expected: true, }, } @@ -51,117 +51,119 @@ func TestAny(t *testing.T) { func TestApply(t *testing.T) { testCases := []struct { + description string enforcement Enforcement - expectedDeviceMacAndIFA bool expectedDeviceIPv6 ScrubStrategyIPV6 expectedDeviceGeo ScrubStrategyGeo - expectedUser ScrubStrategyUser + expectedUserDemographic ScrubStrategyDemographic expectedUserGeo ScrubStrategyGeo - description string }{ { + description: "All Enforced", enforcement: Enforcement{ CCPA: true, COPPA: true, GDPR: true, }, - expectedDeviceMacAndIFA: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserFull, + expectedUserDemographic: ScrubStrategyDemographicAgeAndGender, expectedUserGeo: ScrubStrategyGeoFull, - description: "All Enforced - Most Strict", }, { + description: "CCPA Only", + enforcement: Enforcement{ + CCPA: true, + COPPA: false, + GDPR: false, + }, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedUserDemographic: ScrubStrategyDemographicNone, + expectedUserGeo: ScrubStrategyGeoReducedPrecision, + }, + { + description: "COPPA Only", enforcement: Enforcement{ CCPA: false, COPPA: true, GDPR: false, }, - expectedDeviceMacAndIFA: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserFull, + expectedUserDemographic: ScrubStrategyDemographicAgeAndGender, expectedUserGeo: ScrubStrategyGeoFull, - description: "COPPA", }, { + description: "GDPR Only", enforcement: Enforcement{ CCPA: false, COPPA: false, GDPR: true, }, - expectedDeviceMacAndIFA: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserBuyerIDOnly, + expectedUserDemographic: ScrubStrategyDemographicNone, expectedUserGeo: ScrubStrategyGeoReducedPrecision, - description: "GDPR", - }, - { - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: false, - }, - expectedDeviceMacAndIFA: false, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserBuyerIDOnly, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - description: "CCPA", }, } for _, test := range testCases { req := &openrtb.BidRequest{ - Device: &openrtb.Device{DIDSHA1: "before"}, - User: &openrtb.User{ID: "before"}, + Device: &openrtb.Device{}, + User: &openrtb.User{}, } - device := &openrtb.Device{DIDSHA1: "after"} - user := &openrtb.User{ID: "after"} + replacedDevice := &openrtb.Device{} + replacedUser := &openrtb.User{} m := &mockScrubber{} - m.On("ScrubDevice", req.Device, test.expectedDeviceMacAndIFA, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(device).Once() - m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(user).Once() + m.On("ScrubDevice", req.Device, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once() + m.On("ScrubUser", req.User, test.expectedUserDemographic, test.expectedUserGeo).Return(replacedUser).Once() test.enforcement.apply(req, m) m.AssertExpectations(t) - assert.Equal(t, device, req.Device, "Device Set Correctly") - assert.Equal(t, user, req.User, "User Set Correctly") + assert.Same(t, replacedDevice, req.Device, "Device") + assert.Same(t, replacedUser, req.User, "User") } } func TestApplyNoneApplicable(t *testing.T) { - enforcement := Enforcement{} - device := &openrtb.Device{DIDSHA1: "original"} - user := &openrtb.User{ID: "original"} - req := &openrtb.BidRequest{ - Device: device, - User: user, - } + req := &openrtb.BidRequest{} m := &mockScrubber{} + enforcement := Enforcement{ + CCPA: false, + COPPA: false, + GDPR: false, + } enforcement.apply(req, m) m.AssertNotCalled(t, "ScrubDevice") m.AssertNotCalled(t, "ScrubUser") - assert.Equal(t, device, req.Device, "Device Set Correctly") - assert.Equal(t, user, req.User, "User Set Correctly") +} + +func TestApplyNil(t *testing.T) { + m := &mockScrubber{} + + enforcement := Enforcement{} + enforcement.apply(nil, m) + + m.AssertNotCalled(t, "ScrubDevice") + m.AssertNotCalled(t, "ScrubUser") } type mockScrubber struct { mock.Mock } -func (m *mockScrubber) ScrubDevice(device *openrtb.Device, macAndIFA bool, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { - args := m.Called(device, macAndIFA, ipv6, geo) +func (m *mockScrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { + args := m.Called(device, ipv6, geo) return args.Get(0).(*openrtb.Device) } -func (m *mockScrubber) ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User { - args := m.Called(user, strategy, geo) +func (m *mockScrubber) ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User { + args := m.Called(user, demographic, geo) return args.Get(0).(*openrtb.User) } diff --git a/privacy/scrubber.go b/privacy/scrubber.go index 916b660dcc5..45b79c20a4e 100644 --- a/privacy/scrubber.go +++ b/privacy/scrubber.go @@ -34,24 +34,21 @@ const ( ScrubStrategyGeoReducedPrecision ) -// ScrubStrategyUser defines the approach to scrub PII from user data. -type ScrubStrategyUser int +// ScrubStrategyDemographic defines the approach to non-location demographic data. +type ScrubStrategyDemographic int const ( - // ScrubStrategyUserNone does not remove user data. - ScrubStrategyUserNone ScrubStrategyUser = iota + // ScrubStrategyDemographicNone does not remove non-location demographic data. + ScrubStrategyDemographicNone ScrubStrategyDemographic = iota - // ScrubStrategyUserFull removes the user's buyer id, exchange id year of birth, and gender. - ScrubStrategyUserFull - - // ScrubStrategyUserBuyerIDOnly removes the user's buyer id. - ScrubStrategyUserBuyerIDOnly + // ScrubStrategyDemographicAgeAndGender removes age and gender data. + ScrubStrategyDemographicAgeAndGender ) // Scrubber removes PII from parts of an OpenRTB request. type Scrubber interface { - ScrubDevice(device *openrtb.Device, macAndIFA bool, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device - ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User + ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device + ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User } type scrubber struct{} @@ -61,25 +58,21 @@ func NewScrubber() Scrubber { return scrubber{} } -func (scrubber) ScrubDevice(device *openrtb.Device, macAndIFA bool, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { +func (scrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { if device == nil { return nil } deviceCopy := *device - deviceCopy.DIDMD5 = "" deviceCopy.DIDSHA1 = "" deviceCopy.DPIDMD5 = "" deviceCopy.DPIDSHA1 = "" + deviceCopy.IFA = "" + deviceCopy.MACMD5 = "" + deviceCopy.MACSHA1 = "" deviceCopy.IP = scrubIPV4(device.IP) - if macAndIFA { - deviceCopy.MACSHA1 = "" - deviceCopy.MACMD5 = "" - deviceCopy.IFA = "" - } - switch ipv6 { case ScrubStrategyIPV6Lowest16: deviceCopy.IPv6 = scrubIPV6Lowest16Bits(device.IPv6) @@ -97,21 +90,19 @@ func (scrubber) ScrubDevice(device *openrtb.Device, macAndIFA bool, ipv6 ScrubSt return &deviceCopy } -func (scrubber) ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User { +func (scrubber) ScrubUser(user *openrtb.User, demographic ScrubStrategyDemographic, geo ScrubStrategyGeo) *openrtb.User { if user == nil { return nil } userCopy := *user + userCopy.BuyerUID = "" + userCopy.ID = "" - switch strategy { - case ScrubStrategyUserFull: - userCopy.BuyerUID = "" - userCopy.ID = "" + switch demographic { + case ScrubStrategyDemographicAgeAndGender: userCopy.Yob = 0 userCopy.Gender = "" - case ScrubStrategyUserBuyerIDOnly: - userCopy.BuyerUID = "" } switch geo { @@ -169,13 +160,7 @@ func scrubGeoFull(geo *openrtb.Geo) *openrtb.Geo { return nil } - geoCopy := *geo - geoCopy.Lat = 0 - geoCopy.Lon = 0 - geoCopy.Metro = "" - geoCopy.City = "" - geoCopy.ZIP = "" - return &geoCopy + return &openrtb.Geo{} } func scrubGeoPrecision(geo *openrtb.Geo) *openrtb.Geo { diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go index 168fb5fb23e..2d5ee667538 100644 --- a/privacy/scrubber_test.go +++ b/privacy/scrubber_test.go @@ -28,13 +28,13 @@ func TestScrubDevice(t *testing.T) { } testCases := []struct { + description string expected *openrtb.Device - isMacAndIFA bool ipv6 ScrubStrategyIPV6 geo ScrubStrategyGeo - description string }{ { + description: "IPv6 Lowest 32 & Geo Full", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -47,12 +47,11 @@ func TestScrubDevice(t *testing.T) { IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", Geo: &openrtb.Geo{}, }, - isMacAndIFA: true, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoFull, - description: "Full Scrubbing", + ipv6: ScrubStrategyIPV6Lowest32, + geo: ScrubStrategyGeoFull, }, { + description: "IPv6 Lowest 16 & Geo Full", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -65,12 +64,11 @@ func TestScrubDevice(t *testing.T) { IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", Geo: &openrtb.Geo{}, }, - isMacAndIFA: true, - ipv6: ScrubStrategyIPV6Lowest16, - geo: ScrubStrategyGeoFull, - description: "IPv6 Lowest 16", + ipv6: ScrubStrategyIPV6Lowest16, + geo: ScrubStrategyGeoFull, }, { + description: "IPv6 None & Geo Full", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -83,12 +81,11 @@ func TestScrubDevice(t *testing.T) { IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", Geo: &openrtb.Geo{}, }, - isMacAndIFA: true, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoFull, - description: "IPv6 None", + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoFull, }, { + description: "IPv6 Lowest 32 & Geo Reduced", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -107,12 +104,57 @@ func TestScrubDevice(t *testing.T) { ZIP: "some zip", }, }, - isMacAndIFA: true, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoReducedPrecision, - description: "Geo Reduced Precision", + ipv6: ScrubStrategyIPV6Lowest32, + geo: ScrubStrategyGeoReducedPrecision, + }, + { + description: "IPv6 Lowest 16 & Geo Reduced", + expected: &openrtb.Device{ + DIDMD5: "", + DIDSHA1: "", + DPIDMD5: "", + DPIDSHA1: "", + MACSHA1: "", + MACMD5: "", + IFA: "", + IP: "1.2.3.0", + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", + Geo: &openrtb.Geo{ + Lat: 123.46, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, + }, + ipv6: ScrubStrategyIPV6Lowest16, + geo: ScrubStrategyGeoReducedPrecision, + }, + { + description: "IPv6 None & Geo Reduced", + expected: &openrtb.Device{ + DIDMD5: "", + DIDSHA1: "", + DPIDMD5: "", + DPIDSHA1: "", + MACSHA1: "", + MACMD5: "", + IFA: "", + IP: "1.2.3.0", + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", + Geo: &openrtb.Geo{ + Lat: 123.46, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, + }, + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoReducedPrecision, }, { + description: "IPv6 Lowest 32 & Geo None", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -131,41 +173,72 @@ func TestScrubDevice(t *testing.T) { ZIP: "some zip", }, }, - isMacAndIFA: true, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoNone, - description: "Geo None", + ipv6: ScrubStrategyIPV6Lowest32, + geo: ScrubStrategyGeoNone, }, { + description: "IPv6 Lowest 16 & Geo None", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", DPIDMD5: "", DPIDSHA1: "", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", + MACSHA1: "", + MACMD5: "", + IFA: "", IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", - Geo: &openrtb.Geo{}, + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", + Geo: &openrtb.Geo{ + Lat: 123.456, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, }, - isMacAndIFA: false, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoFull, - description: "Without MAC Address And IFA Scrubbing", + ipv6: ScrubStrategyIPV6Lowest16, + geo: ScrubStrategyGeoNone, + }, + { + description: "IPv6 None & Geo None", + expected: &openrtb.Device{ + DIDMD5: "", + DIDSHA1: "", + DPIDMD5: "", + DPIDSHA1: "", + MACSHA1: "", + MACMD5: "", + IFA: "", + IP: "1.2.3.0", + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", + Geo: &openrtb.Geo{ + Lat: 123.456, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, + }, + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoNone, }, } for _, test := range testCases { - result := NewScrubber().ScrubDevice(device, test.isMacAndIFA, test.ipv6, test.geo) + result := NewScrubber().ScrubDevice(device, test.ipv6, test.geo) assert.Equal(t, test.expected, result, test.description) } } +func TestScrubDeviceNil(t *testing.T) { + result := NewScrubber().ScrubDevice(nil, ScrubStrategyIPV6None, ScrubStrategyGeoNone) + assert.Nil(t, result) +} + func TestScrubUser(t *testing.T) { user := &openrtb.User{ - BuyerUID: "anyBuyerUID", ID: "anyID", + BuyerUID: "anyBuyerUID", Yob: 42, Gender: "anyGender", Geo: &openrtb.Geo{ @@ -179,52 +252,77 @@ func TestScrubUser(t *testing.T) { testCases := []struct { expected *openrtb.User - strategy ScrubStrategyUser + demographic ScrubStrategyDemographic geo ScrubStrategyGeo description string }{ { + description: "Demographic Age And Gender & Geo Full", expected: &openrtb.User{ - BuyerUID: "", ID: "", + BuyerUID: "", Yob: 0, Gender: "", Geo: &openrtb.Geo{}, }, - strategy: ScrubStrategyUserFull, + demographic: ScrubStrategyDemographicAgeAndGender, geo: ScrubStrategyGeoFull, - description: "Full Scrubbing", }, { + description: "Demographic Age And Gender & Geo Reduced", expected: &openrtb.User{ + ID: "", BuyerUID: "", - ID: "anyID", - Yob: 42, - Gender: "anyGender", - Geo: &openrtb.Geo{}, + Yob: 0, + Gender: "", + Geo: &openrtb.Geo{ + Lat: 123.46, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, }, - strategy: ScrubStrategyUserBuyerIDOnly, - geo: ScrubStrategyGeoFull, - description: "User Buyer ID Only", + demographic: ScrubStrategyDemographicAgeAndGender, + geo: ScrubStrategyGeoReducedPrecision, + }, + { + description: "Demographic Age And Gender & Geo None", + expected: &openrtb.User{ + ID: "", + BuyerUID: "", + Yob: 0, + Gender: "", + Geo: &openrtb.Geo{ + Lat: 123.456, + Lon: 678.89, + Metro: "some metro", + City: "some city", + ZIP: "some zip", + }, + }, + demographic: ScrubStrategyDemographicAgeAndGender, + geo: ScrubStrategyGeoNone, }, { + description: "Demographic None & Geo Full", expected: &openrtb.User{ - BuyerUID: "anyBuyerUID", - ID: "anyID", + ID: "", + BuyerUID: "", Yob: 42, Gender: "anyGender", Geo: &openrtb.Geo{}, }, - strategy: ScrubStrategyUserNone, + demographic: ScrubStrategyDemographicNone, geo: ScrubStrategyGeoFull, - description: "User None", }, { + description: "Demographic None & Geo Reduced", expected: &openrtb.User{ - BuyerUID: "", ID: "", - Yob: 0, - Gender: "", + BuyerUID: "", + Yob: 42, + Gender: "anyGender", Geo: &openrtb.Geo{ Lat: 123.46, Lon: 678.89, @@ -233,16 +331,16 @@ func TestScrubUser(t *testing.T) { ZIP: "some zip", }, }, - strategy: ScrubStrategyUserFull, + demographic: ScrubStrategyDemographicNone, geo: ScrubStrategyGeoReducedPrecision, - description: "Geo Reduced Precision", }, { + description: "Demographic None & Geo None", expected: &openrtb.User{ - BuyerUID: "", ID: "", - Yob: 0, - Gender: "", + BuyerUID: "", + Yob: 42, + Gender: "anyGender", Geo: &openrtb.Geo{ Lat: 123.456, Lon: 678.89, @@ -251,18 +349,22 @@ func TestScrubUser(t *testing.T) { ZIP: "some zip", }, }, - strategy: ScrubStrategyUserFull, + demographic: ScrubStrategyDemographicNone, geo: ScrubStrategyGeoNone, - description: "Geo None", }, } for _, test := range testCases { - result := NewScrubber().ScrubUser(user, test.strategy, test.geo) + result := NewScrubber().ScrubUser(user, test.demographic, test.geo) assert.Equal(t, test.expected, result, test.description) } } +func TestScrubUserNil(t *testing.T) { + result := NewScrubber().ScrubUser(nil, ScrubStrategyDemographicNone, ScrubStrategyGeoNone) + assert.Nil(t, result) +} + func TestScrubIPV4(t *testing.T) { testCases := []struct { IP string From 2481fea01f9b339f5455d93a370e2550c58ac087 Mon Sep 17 00:00:00 2001 From: Arne Schulz Date: Mon, 11 May 2020 17:09:33 +0200 Subject: [PATCH 076/318] Add Adapter Orbidder (#1275) Co-authored-by: Volk, Rainer Co-authored-by: RainerVolk4014 <53347752+RainerVolk4014@users.noreply.github.com> Co-authored-by: rvolk <> Co-authored-by: Hendrik Iseke Co-authored-by: hendrikiseke1979 <53309111+hendrikiseke1979@users.noreply.github.com> --- adapters/orbidder/orbidder.go | 127 ++++++++++++++++++ adapters/orbidder/orbidder_test.go | 25 ++++ .../exemplary/simple-app-banner.json | 111 +++++++++++++++ .../orbiddertest/params/race/banner.json | 5 + .../supplemental/dsp-bad-request-example.json | 78 +++++++++++ .../dsp-bad-response-example.json | 78 +++++++++++ .../dsp-internal-server-error-example.json | 78 +++++++++++ .../dsp-invalid-accountid-example.json | 78 +++++++++++ .../supplemental/empty-imp-request-error.json | 19 +++ .../supplemental/ext-unmarshall-error.json | 32 +++++ .../supplemental/no-content-response.json | 73 ++++++++++ .../supplemental/valid-and-invalid-imps.json | 123 +++++++++++++++++ adapters/orbidder/params_test.go | 65 +++++++++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_orbidder.go | 8 ++ static/bidder-info/orbidder.yaml | 9 ++ static/bidder-params/orbidder.json | 24 ++++ usersync/usersyncers/syncer_test.go | 1 + 20 files changed, 939 insertions(+) create mode 100644 adapters/orbidder/orbidder.go create mode 100644 adapters/orbidder/orbidder_test.go create mode 100644 adapters/orbidder/orbiddertest/exemplary/simple-app-banner.json create mode 100644 adapters/orbidder/orbiddertest/params/race/banner.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/dsp-bad-request-example.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/dsp-bad-response-example.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/dsp-internal-server-error-example.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/dsp-invalid-accountid-example.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/empty-imp-request-error.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/ext-unmarshall-error.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/no-content-response.json create mode 100644 adapters/orbidder/orbiddertest/supplemental/valid-and-invalid-imps.json create mode 100644 adapters/orbidder/params_test.go create mode 100644 openrtb_ext/imp_orbidder.go create mode 100644 static/bidder-info/orbidder.yaml create mode 100644 static/bidder-params/orbidder.json diff --git a/adapters/orbidder/orbidder.go b/adapters/orbidder/orbidder.go new file mode 100644 index 00000000000..27b41c89857 --- /dev/null +++ b/adapters/orbidder/orbidder.go @@ -0,0 +1,127 @@ +package orbidder + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" +) + +type OrbidderAdapter struct { + endpoint string +} + +// MakeRequests makes the HTTP requests which should be made to fetch bids from orbidder. +func (rcv *OrbidderAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var validImps []openrtb.Imp + + // check if imps exists, if not return error and do send request to orbidder. + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impressions in request", + }} + } + + // validate imps + for _, imp := range request.Imp { + if err := preprocess(&imp); err != nil { + errs = append(errs, err) + continue + } + validImps = append(validImps, imp) + } + + if len(validImps) == 0 { + return nil, errs + } + + //set imp array to only valid imps + request.Imp = validImps + + requestBodyJSON, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return []*adapters.RequestData{{ + Method: "POST", + Uri: rcv.endpoint, + Body: requestBodyJSON, + Headers: headers, + }}, errs +} + +func preprocess(imp *openrtb.Imp) error { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + var orbidderExt openrtb_ext.ExtImpOrbidder + if err := json.Unmarshal(bidderExt.Bidder, &orbidderExt); err != nil { + return &errortypes.BadInput{ + Message: "Wrong orbidder bidder ext: " + err.Error(), + } + } + + return nil +} + +// MakeBids unpacks server response into Bids. +func (rcv OrbidderAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode >= http.StatusInternalServerError { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error.", response.StatusCode), + }} + } + + if response.StatusCode >= http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp.", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Bad response from dsp.", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + + for _, seatBid := range bidResp.SeatBid { + for _, bid := range seatBid.Bid { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: openrtb_ext.BidTypeBanner, + }) + } + } + return bidResponse, nil +} + +func NewOrbidderBidder(endpoint string) *OrbidderAdapter { + return &OrbidderAdapter{ + endpoint: endpoint, + } +} diff --git a/adapters/orbidder/orbidder_test.go b/adapters/orbidder/orbidder_test.go new file mode 100644 index 00000000000..e0f7a6b4265 --- /dev/null +++ b/adapters/orbidder/orbidder_test.go @@ -0,0 +1,25 @@ +package orbidder + +import ( + "encoding/json" + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestUnmarshalOrbidderExtImp(t *testing.T) { + ext := json.RawMessage(`{"accountId":"orbidder-test", "placementId":"center-banner", "bidfloor": 0.1}`) + impExt := new(openrtb_ext.ExtImpOrbidder) + + assert.NoError(t, json.Unmarshal(ext, impExt)) + assert.Equal(t, &openrtb_ext.ExtImpOrbidder{ + AccountId: "orbidder-test", + PlacementId: "center-banner", + BidFloor: 0.1, + }, impExt) +} + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "orbiddertest", NewOrbidderBidder("https://orbidder-test")) +} diff --git a/adapters/orbidder/orbiddertest/exemplary/simple-app-banner.json b/adapters/orbidder/orbiddertest/exemplary/simple-app-banner.json new file mode 100644 index 00000000000..8697bff3a92 --- /dev/null +++ b/adapters/orbidder/orbiddertest/exemplary/simple-app-banner.json @@ -0,0 +1,111 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/orbidder/orbiddertest/params/race/banner.json b/adapters/orbidder/orbiddertest/params/race/banner.json new file mode 100644 index 00000000000..bdb0e010e05 --- /dev/null +++ b/adapters/orbidder/orbiddertest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "accountId": "orbidder-test", + "placementId": "center-banner", + "bidfloor": 0.1 +} \ No newline at end of file diff --git a/adapters/orbidder/orbiddertest/supplemental/dsp-bad-request-example.json b/adapters/orbidder/orbiddertest/supplemental/dsp-bad-request-example.json new file mode 100644 index 00000000000..69496c4ff3f --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/dsp-bad-request-example.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Bad request to dsp.", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/dsp-bad-response-example.json b/adapters/orbidder/orbiddertest/supplemental/dsp-bad-response-example.json new file mode 100644 index 00000000000..d1c57a54a9e --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/dsp-bad-response-example.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 300, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 300. Bad response from dsp.", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/dsp-internal-server-error-example.json b/adapters/orbidder/orbiddertest/supplemental/dsp-internal-server-error-example.json new file mode 100644 index 00000000000..20ea36ab38c --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/dsp-internal-server-error-example.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Dsp server internal error.", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/dsp-invalid-accountid-example.json b/adapters/orbidder/orbiddertest/supplemental/dsp-invalid-accountid-example.json new file mode 100644 index 00000000000..6bc0482dd0c --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/dsp-invalid-accountid-example.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 403, + "body": { + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 403. Bad request to dsp.", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/empty-imp-request-error.json b/adapters/orbidder/orbiddertest/supplemental/empty-imp-request-error.json new file mode 100644 index 00000000000..0c5cf6d2faa --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/empty-imp-request-error.json @@ -0,0 +1,19 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + ], + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impressions in request", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/ext-unmarshall-error.json b/adapters/orbidder/orbiddertest/supplemental/ext-unmarshall-error.json new file mode 100644 index 00000000000..447e2985f92 --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/ext-unmarshall-error.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "ext": { + "bidder": { + "accountId": [] + } + } + } + ], + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Wrong orbidder bidder ext: json: cannot unmarshal array into Go struct field ExtImpOrbidder.accountId of type string", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/no-content-response.json b/adapters/orbidder/orbiddertest/supplemental/no-content-response.json new file mode 100644 index 00000000000..f3b1b287da7 --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/no-content-response.json @@ -0,0 +1,73 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": { + } + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/orbidder/orbiddertest/supplemental/valid-and-invalid-imps.json b/adapters/orbidder/orbiddertest/supplemental/valid-and-invalid-imps.json new file mode 100644 index 00000000000..b6db9f48ee3 --- /dev/null +++ b/adapters/orbidder/orbiddertest/supplemental/valid-and-invalid-imps.json @@ -0,0 +1,123 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + }, + { + "id": "test-imp-id-INVALID", + "banner": { + "format": [{}] + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://orbidder-test", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "orbidder-test", + "placementId": "test-placement", + "bidfloor": 0.1 + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} diff --git a/adapters/orbidder/params_test.go b/adapters/orbidder/params_test.go new file mode 100644 index 00000000000..19c4ed8d9d4 --- /dev/null +++ b/adapters/orbidder/params_test.go @@ -0,0 +1,65 @@ +package orbidder + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +// This file actually intends to test static/bidder-params/orbidder.json +// +// These also validate the format of the external API: request.imp[i].ext.orbidder + +// TestValidParams makes sure that the orbidder schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderOrbidder, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected orbidder params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the orbidder schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderOrbidder, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placementId":"123","accountId":"orbidder-test"}`, + `{"placementId":"123","accountId":"orbidder-test","bidfloor":0.5}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"placement_id":"123"}`, + `{"placementId":123}`, + `{"placementId":"123"}`, + `{"account_id":"orbidder-test"}`, + `{"accountId":123}`, + `{"accountId":"orbidder-test"}`, + `{"placementId":123,"account_id":"orbidder-test"}`, + `{"placementId":"123","account_id":123}`, + `{"placementId":"123","accountId":"orbidder-test","bidfloor":"0.5"}`, + `{"placementId":"123","bidfloor":"0.5"}`, + `{"accountId":"orbidder-test","bidfloor":"0.5"}`, +} diff --git a/config/config.go b/config/config.go index a7132edbc81..ccdb5c0625b 100755 --- a/config/config.go +++ b/config/config.go @@ -729,6 +729,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs") v.SetDefault("adapters.ninthdecimal.endpoint", "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid") + v.SetDefault("adapters.orbidder.endpoint", "https://orbidder.otto.de/openrtb2") v.SetDefault("adapters.pubmatic.endpoint", "https://hbopenbid.pubmatic.com/translator?source=prebid-server") v.SetDefault("adapters.pubnative.endpoint", "http://dsp.pubnative.net/bid/v1/request") v.SetDefault("adapters.pulsepoint.endpoint", "http://bid.contextweb.com/header/s/ortb/prebid-s2s") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 17814b3639a..2029d1a7553 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -46,6 +46,7 @@ import ( "github.com/prebid/prebid-server/adapters/nanointeractive" "github.com/prebid/prebid-server/adapters/ninthdecimal" "github.com/prebid/prebid-server/adapters/openx" + "github.com/prebid/prebid-server/adapters/orbidder" "github.com/prebid/prebid-server/adapters/pubmatic" "github.com/prebid/prebid-server/adapters/pubnative" "github.com/prebid/prebid-server/adapters/pulsepoint" @@ -119,6 +120,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint), openrtb_ext.BidderNinthDecimal: ninthdecimal.NewNinthDecimalBidder(cfg.Adapters[string(openrtb_ext.BidderNinthDecimal)].Endpoint), + openrtb_ext.BidderOrbidder: orbidder.NewOrbidderBidder(cfg.Adapters[string(openrtb_ext.BidderOrbidder)].Endpoint), openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint), openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint), openrtb_ext.BidderPubnative: pubnative.NewPubnativeBidder(cfg.Adapters[string(openrtb_ext.BidderPubnative)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index c9b7f7a0519..01112091cdf 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -64,6 +64,7 @@ const ( BidderNanoInteractive BidderName = "nanointeractive" BidderNinthDecimal BidderName = "ninthdecimal" BidderOpenx BidderName = "openx" + BidderOrbidder BidderName = "orbidder" BidderPubmatic BidderName = "pubmatic" BidderPubnative BidderName = "pubnative" BidderPulsepoint BidderName = "pulsepoint" @@ -134,6 +135,7 @@ var BidderMap = map[string]BidderName{ "nanointeractive": BidderNanoInteractive, "ninthdecimal": BidderNinthDecimal, "openx": BidderOpenx, + "orbidder": BidderOrbidder, "pubmatic": BidderPubmatic, "pubnative": BidderPubnative, "pulsepoint": BidderPulsepoint, diff --git a/openrtb_ext/imp_orbidder.go b/openrtb_ext/imp_orbidder.go new file mode 100644 index 00000000000..ad141bdbcdf --- /dev/null +++ b/openrtb_ext/imp_orbidder.go @@ -0,0 +1,8 @@ +package openrtb_ext + +// ExtImpOrbidder defines the contract for bidrequest.imp[i].ext.openx +type ExtImpOrbidder struct { + AccountId string `json:"accountId"` + PlacementId string `json:"placementId"` + BidFloor float64 `json:"bidfloor"` +} diff --git a/static/bidder-info/orbidder.yaml b/static/bidder-info/orbidder.yaml new file mode 100644 index 00000000000..c683087d197 --- /dev/null +++ b/static/bidder-info/orbidder.yaml @@ -0,0 +1,9 @@ +maintainer: + email: "realtime-siggi@otto.de" +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner \ No newline at end of file diff --git a/static/bidder-params/orbidder.json b/static/bidder-params/orbidder.json new file mode 100644 index 00000000000..d986b23284e --- /dev/null +++ b/static/bidder-params/orbidder.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Orbidder Adapter Params", + "description": "A schema which validates params accepted by the Orbidder adapter", + + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "The marketer's accountId." + }, + "placementId": { + "type": "string", + "description": "The placementId of the ad unit." + }, + "bidfloor": { + "type": "number", + "description": "The minimum CPM price in EUR.", + "minimum": 0 + } + }, + + "required": ["accountId", "placementId"] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 44ff15bd5fe..88c1b9467a6 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -83,6 +83,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderKubient: true, openrtb_ext.BidderPubnative: true, openrtb_ext.BidderKidoz: true, + openrtb_ext.BidderOrbidder: true, } for bidder, config := range cfg.Adapters { From 4257bf1ed9dc1dd2647c4f463890b95a775147ab Mon Sep 17 00:00:00 2001 From: Jimmy Tu Date: Tue, 12 May 2020 07:27:03 -0700 Subject: [PATCH 077/318] Added OpenX Bidder adapter documentation (#1291) --- docs/bidders/openx.md | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/bidders/openx.md diff --git a/docs/bidders/openx.md b/docs/bidders/openx.md new file mode 100644 index 00000000000..c366db3ab61 --- /dev/null +++ b/docs/bidders/openx.md @@ -0,0 +1,62 @@ +# OpenX Bidder + +OpenX supports the following parameters: + +| property | type | required? | description | example | +|----------|------|-----------|-------------|---------| +| unit | string | required | The ad unit id | "10092842" | +| delDomain | string | required | The delivery domain for the customer | "sademo-d.openx.net" | +| customFloor | number | optional | The minimum CPM price in USD | 1.50 - sets a $1.50 floor | +| customParams | object | optional | User-defined targeting key-value pairs | {key1: "v1", key2: ["v2","v3"]} | + +If you have any questions regarding setting up, please reach out to your account manager or + + +## Test Request + +### App Impression Object +``` +{ + "id": "test-impression-id", + "banner": { + "format": [ + { + "w": 480, + "h": 300 + }, + { + "w": 480, + "h": 320 + } + ] + }, + "ext": { + "openx": { + "delDomain": "mobile-d.openx.net", + "unit": "541028953" + } + } +} +``` + + +### Web +``` +{ + "id": "div1", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "openx": { + "unit": "540949380", + "delDomain": "sademo-d.openx.net" + }, + } +} +``` \ No newline at end of file From 93b8a0edb8e418f4e360469d6d136995ddf45dc1 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 13 May 2020 10:12:14 -0700 Subject: [PATCH 078/318] OpenX adapter: Pass rewarded video flag (#1290) --- adapters/openx/openx.go | 8 ++ .../openxtest/exemplary/video-rewarded.json | 102 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 adapters/openx/openxtest/exemplary/video-rewarded.json diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go index 63e8e697869..63297d0a4ee 100644 --- a/adapters/openx/openx.go +++ b/adapters/openx/openx.go @@ -142,6 +142,14 @@ func preprocess(imp *openrtb.Imp, reqExt *openxReqExt) error { } } + if imp.Video != nil { + if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory == 1 { + imp.Video.Ext = json.RawMessage(`{"rewarded":1}`) + } else { + imp.Video.Ext = nil + } + } + return nil } diff --git a/adapters/openx/openxtest/exemplary/video-rewarded.json b/adapters/openx/openxtest/exemplary/video-rewarded.json new file mode 100644 index 00000000000..b16a92f23ac --- /dev/null +++ b/adapters/openx/openxtest/exemplary/video-rewarded.json @@ -0,0 +1,102 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "instl": 1, + "ext": { + "bidder": { + "unit": "539439964", + "delDomain": "se-demo-d.openx.net" + }, + "prebid": { + "is_rewarded_inventory": 1 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://rtb.openx.net/prebid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576, + "ext": { + "rewarded": 1 + } + }, + "tagid": "539439964", + "instl": 1 + } + ], + "ext": { + "bc": "hb_pbs_1.0.0", + "delDomain": "se-demo-d.openx.net" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "openx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 1024, + "h": 576 + }] + } + ] + } + } + } + ], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] + } + ] +} From c18a2d86c5bad5987008b3df485072ebf471683e Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Thu, 14 May 2020 07:35:49 -0700 Subject: [PATCH 079/318] Bugfix for missing fields in imp.video (#1297) Co-authored-by: Veronika Solovei --- endpoints/openrtb2/video_auction.go | 8 +++----- endpoints/openrtb2/video_auction_test.go | 25 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index c7316604d73..cf764bc9d2d 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -381,11 +381,9 @@ func max(a, b int) int { } func createImpressionTemplate(imp openrtb.Imp, video *openrtb.Video) openrtb.Imp { - imp.Video = &openrtb.Video{} - imp.Video.W = video.W - imp.Video.H = video.H - imp.Video.Protocols = video.Protocols - imp.Video.MIMEs = video.MIMEs + //for every new impression we need to have it's own copy of video object, because we customize it in further processing + newVideo := *video + imp.Video = &newVideo return imp } diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index ec525c6ff08..38c9dc3f685 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -1005,6 +1005,31 @@ func TestHandleErrorDebugLog(t *testing.T) { assert.NotEmpty(t, debugLog.CacheKey, "DebugLog CacheKey value should have been set") } +func TestCreateImpressionTemplate(t *testing.T) { + + imp := openrtb.Imp{} + imp.Video = &openrtb.Video{} + imp.Video.Protocols = []openrtb.Protocol{1, 2} + imp.Video.MIMEs = []string{"video/mp4"} + imp.Video.H = 200 + imp.Video.W = 400 + imp.Video.PlaybackMethod = []openrtb.PlaybackMethod{5, 6} + + video := openrtb.Video{} + video.Protocols = []openrtb.Protocol{3, 4} + video.MIMEs = []string{"video/flv"} + video.H = 300 + video.W = 0 + video.PlaybackMethod = []openrtb.PlaybackMethod{7, 8} + + res := createImpressionTemplate(imp, &video) + assert.Equal(t, res.Video.Protocols, []openrtb.Protocol{3, 4}, "Incorrect video protocols") + assert.Equal(t, res.Video.MIMEs, []string{"video/flv"}, "Incorrect video MIMEs") + assert.Equal(t, int(res.Video.H), 300, "Incorrect video height") + assert.Equal(t, int(res.Video.W), 0, "Incorrect video width") + assert.Equal(t, res.Video.PlaybackMethod, []openrtb.PlaybackMethod{7, 8}, "Incorrect video playback method") +} + func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} From 9f7ed209b685c3135eee56a64f963476268716a9 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 14 May 2020 17:44:06 +0300 Subject: [PATCH 080/318] Add cpmOverride (#1289) * Add cpmOverride Enabled `request.ext.rubicon.debug.cpmOverride` and `request.imp[].ext.rubicon.debug.cpmOverride` processing. Updates tests * Remove unnecessary error checks and add shallow copy * Fixed same pointer --- adapters/rubicon/rubicon.go | 71 ++++++++++++++++++++--- adapters/rubicon/rubicon_test.go | 97 +++++++++++++++++++++++++++++++- openrtb_ext/imp_rubicon.go | 6 ++ 3 files changed, 163 insertions(+), 11 deletions(-) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index dad85ee1184..ee737bd05ea 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -48,6 +48,18 @@ type rubiconParams struct { Video rubiconVideoParams `json:"video"` } +type bidRequestExt struct { + Rubicon bidRequestExtRubicon `json:"rubicon,omitempty"` +} + +type bidRequestExtRubicon struct { + Debug bidRequestExtRubiconDebug `json:"debug,omitempty"` +} + +type bidRequestExtRubiconDebug struct { + CpmOverride float64 `json:"cpmOverride,omitempty"` +} + type rubiconImpExtRPTrack struct { Mint string `json:"mint"` MintVersion string `json:"mint_version"` @@ -578,6 +590,7 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap requestImpCopy := request.Imp + rubiconRequest := *request for i := 0; i < numRequests; i++ { thisImp := requestImpCopy[i] @@ -677,14 +690,14 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap errs = append(errs, err) continue } - request.User = &userCopy + rubiconRequest.User = &userCopy } if request.Device != nil { deviceCopy := *request.Device deviceExt := rubiconDeviceExt{RP: rubiconDeviceExtRP{PixelRatio: request.Device.PxRatio}} deviceCopy.Ext, err = json.Marshal(&deviceExt) - request.Device = &deviceCopy + rubiconRequest.Device = &deviceCopy } isVideo := isVideo(thisImp) @@ -732,28 +745,28 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap siteCopy.Ext, err = json.Marshal(&siteExt) siteCopy.Publisher = &openrtb.Publisher{} siteCopy.Publisher.Ext, err = json.Marshal(&pubExt) - request.Site = &siteCopy + rubiconRequest.Site = &siteCopy } if request.App != nil { appCopy := *request.App appCopy.Ext, err = json.Marshal(&siteExt) appCopy.Publisher = &openrtb.Publisher{} appCopy.Publisher.Ext, err = json.Marshal(&pubExt) - request.App = &appCopy + rubiconRequest.App = &appCopy } reqBadv := request.BAdv if reqBadv != nil { if len(reqBadv) > badvLimitSize { - request.BAdv = reqBadv[:badvLimitSize] + rubiconRequest.BAdv = reqBadv[:badvLimitSize] } } - request.Imp = []openrtb.Imp{thisImp} - request.Cur = nil - request.Ext = nil + rubiconRequest.Imp = []openrtb.Imp{thisImp} + rubiconRequest.Cur = nil + rubiconRequest.Ext = nil - reqJSON, err := json.Marshal(request) + reqJSON, err := json.Marshal(rubiconRequest) if err != nil { errs = append(errs, err) continue @@ -900,9 +913,22 @@ func (a *RubiconAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalR bidType = openrtb_ext.BidTypeVideo } + impToCpmOverride := mapImpIdToCpmOverride(internalRequest.Imp) + cmpOverride := cmpOverrideFromBidRequest(internalRequest) + for _, sb := range bidResp.SeatBid { for i := 0; i < len(sb.Bid); i++ { bid := sb.Bid[i] + + bidCmpOverride, ok := impToCpmOverride[bid.ImpID] + if !ok || bidCmpOverride == 0 { + bidCmpOverride = cmpOverride + } + + if bidCmpOverride > 0 { + bid.Price = bidCmpOverride + } + if bid.Price != 0 { // Since Rubicon XAPI returns only one bid per response // copy response.bidid to openrtb_response.seatbid.bid.bidid @@ -919,3 +945,30 @@ func (a *RubiconAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalR return bidResponse, nil } + +func cmpOverrideFromBidRequest(bidRequest *openrtb.BidRequest) float64 { + var bidRequestExt bidRequestExt + if err := json.Unmarshal(bidRequest.Ext, &bidRequestExt); err != nil { + return 0 + } + + return bidRequestExt.Rubicon.Debug.CpmOverride +} + +func mapImpIdToCpmOverride(imps []openrtb.Imp) map[string]float64 { + impIdToCmpOverride := make(map[string]float64) + for _, imp := range imps { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + continue + } + + var rubiconExt openrtb_ext.ExtImpRubicon + if err := json.Unmarshal(bidderExt.Bidder, &rubiconExt); err != nil { + continue + } + + impIdToCmpOverride[imp.ID] = rubiconExt.Debug.CpmOverride + } + return impIdToCmpOverride +} diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index 96623659d08..7a2cc28896b 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -973,9 +973,9 @@ func TestOpenRTBRequest(t *testing.T) { } assert.Equal(t, request.ID, rpRequest.ID, "Bad Request ID. Expected %s, Got %s", request.ID, rpRequest.ID) - assert.Equal(t, len(request.Imp), len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp)) + assert.Equal(t, 1, len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp)) assert.Nil(t, rpRequest.Cur, "Wrong request.Cur. Expected nil, Got %s", rpRequest.Cur) - assert.Nil(t, request.Ext, "Wrong request.ext. Expected nil, Got %v", request.Ext) + assert.Nil(t, rpRequest.Ext, "Wrong request.ext. Expected nil, Got %v", rpRequest.Ext) if rpRequest.Imp[0].ID == "test-imp-banner-id" { var rpExt rubiconBannerExt @@ -1425,6 +1425,99 @@ func TestOpenRTBStandardResponse(t *testing.T) { assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID) } +func TestOpenRTBResponseOverridePriceFromBidRequest(t *testing.T) { + request := &openrtb.BidRequest{ + ID: "test-request-id", + Imp: []openrtb.Imp{{ + ID: "test-imp-id", + Banner: &openrtb.Banner{ + Format: []openrtb.Format{{ + W: 320, + H: 50, + }}, + }, + Ext: json.RawMessage(`{"bidder": { + "accountId": 2763, + "siteId": 68780, + "zoneId": 327642 + }}`), + }}, + Ext: json.RawMessage(`{"rubicon": { + "debug": { + "cpmOverride" : 10 + }}}`), + } + + requestJson, _ := json.Marshal(request) + reqData := &adapters.RequestData{ + Method: "POST", + Uri: "test-uri", + Body: requestJson, + Headers: nil, + } + + httpResp := &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`), + } + + bidder := new(RubiconAdapter) + bidResponse, errs := bidder.MakeBids(request, reqData, httpResp) + + assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs)) + + assert.Equal(t, float64(10), bidResponse.Bids[0].Bid.Price, + "Expected Price 10. Got: %s", bidResponse.Bids[0].Bid.Price) +} + +func TestOpenRTBResponseOverridePriceFromCorrespondingImp(t *testing.T) { + request := &openrtb.BidRequest{ + ID: "test-request-id", + Imp: []openrtb.Imp{{ + ID: "test-imp-id", + Banner: &openrtb.Banner{ + Format: []openrtb.Format{{ + W: 320, + H: 50, + }}, + }, + Ext: json.RawMessage(`{"bidder": { + "accountId": 2763, + "siteId": 68780, + "zoneId": 327642, + "debug": { + "cpmOverride" : 20 + } + }}`), + }}, + Ext: json.RawMessage(`{"rubicon": { + "debug": { + "cpmOverride" : 10 + }}}`), + } + + requestJson, _ := json.Marshal(request) + reqData := &adapters.RequestData{ + Method: "POST", + Uri: "test-uri", + Body: requestJson, + Headers: nil, + } + + httpResp := &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`), + } + + bidder := new(RubiconAdapter) + bidResponse, errs := bidder.MakeBids(request, reqData, httpResp) + + assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs)) + + assert.Equal(t, float64(20), bidResponse.Bids[0].Bid.Price, + "Expected Price 20. Got: %s", bidResponse.Bids[0].Bid.Price) +} + func TestOpenRTBCopyBidIdFromResponseIfZero(t *testing.T) { request := &openrtb.BidRequest{ ID: "test-request-id", diff --git a/openrtb_ext/imp_rubicon.go b/openrtb_ext/imp_rubicon.go index d588af82184..17585a8ee93 100644 --- a/openrtb_ext/imp_rubicon.go +++ b/openrtb_ext/imp_rubicon.go @@ -12,6 +12,7 @@ type ExtImpRubicon struct { Inventory json.RawMessage `json:"inventory,omitempty"` Visitor json.RawMessage `json:"visitor,omitempty"` Video rubiconVideoParams `json:"video"` + Debug impExtRubiconDebug `json:"debug,omitempty"` } // rubiconVideoParams defines the contract for bidrequest.imp[i].ext.rubicon.video @@ -23,3 +24,8 @@ type rubiconVideoParams struct { Skip int `json:"skip,omitempty"` SkipDelay int `json:"skipdelay,omitempty"` } + +// rubiconVideoParams defines the contract for bidrequest.imp[i].ext.rubicon.debug +type impExtRubiconDebug struct { + CpmOverride float64 `json:"cpmOverride,omitempty"` +} From 6e5d0445ad29a9af1259a175843cb3f531cacd5a Mon Sep 17 00:00:00 2001 From: ddantuonobeintoo <58686785+ddantuonobeintoo@users.noreply.github.com> Date: Thu, 14 May 2020 16:59:44 +0200 Subject: [PATCH 081/318] Add Beintoo adapter (#1274) * Add Beintoo adapter --- adapters/beintoo/beintoo.go | 222 ++++++++++++++++++ adapters/beintoo/beintoo_test.go | 12 + .../beintootest/exemplary/minimal-banner.json | 117 +++++++++ .../beintootest/params/race/banner.json | 4 + .../supplemental/add-bidfloor.json | 42 ++++ .../bad-imp-banner-missing-sizes.json | 32 +++ .../supplemental/bad-imp-ext-tagid-value.json | 33 +++ .../supplemental/build-banner-object.json | 61 +++++ .../invalid-request-no-banner.json | 26 ++ .../invalid-response-no-bids.json | 45 ++++ .../invalid-response-unmarshall-error.json | 63 +++++ .../supplemental/no-imps-in-request.json | 18 ++ .../supplemental/server-error-code.json | 52 ++++ .../supplemental/server-no-content.json | 44 ++++ .../site-domain-and-url-correctly-parsed.json | 61 +++++ adapters/beintoo/params_test.go | 53 +++++ adapters/beintoo/usersync.go | 12 + adapters/beintoo/usersync_test.go | 35 +++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_beintoo.go | 6 + static/bidder-info/beintoo.yaml | 6 + static/bidder-params/beintoo.json | 18 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 26 files changed, 971 insertions(+) create mode 100644 adapters/beintoo/beintoo.go create mode 100644 adapters/beintoo/beintoo_test.go create mode 100644 adapters/beintoo/beintootest/exemplary/minimal-banner.json create mode 100644 adapters/beintoo/beintootest/params/race/banner.json create mode 100644 adapters/beintoo/beintootest/supplemental/add-bidfloor.json create mode 100644 adapters/beintoo/beintootest/supplemental/bad-imp-banner-missing-sizes.json create mode 100644 adapters/beintoo/beintootest/supplemental/bad-imp-ext-tagid-value.json create mode 100644 adapters/beintoo/beintootest/supplemental/build-banner-object.json create mode 100644 adapters/beintoo/beintootest/supplemental/invalid-request-no-banner.json create mode 100644 adapters/beintoo/beintootest/supplemental/invalid-response-no-bids.json create mode 100644 adapters/beintoo/beintootest/supplemental/invalid-response-unmarshall-error.json create mode 100644 adapters/beintoo/beintootest/supplemental/no-imps-in-request.json create mode 100644 adapters/beintoo/beintootest/supplemental/server-error-code.json create mode 100644 adapters/beintoo/beintootest/supplemental/server-no-content.json create mode 100644 adapters/beintoo/beintootest/supplemental/site-domain-and-url-correctly-parsed.json create mode 100644 adapters/beintoo/params_test.go create mode 100644 adapters/beintoo/usersync.go create mode 100644 adapters/beintoo/usersync_test.go create mode 100644 openrtb_ext/imp_beintoo.go create mode 100644 static/bidder-info/beintoo.yaml create mode 100644 static/bidder-params/beintoo.json diff --git a/adapters/beintoo/beintoo.go b/adapters/beintoo/beintoo.go new file mode 100644 index 00000000000..fb511f12075 --- /dev/null +++ b/adapters/beintoo/beintoo.go @@ -0,0 +1,222 @@ +package beintoo + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type BeintooAdapter struct { + endpoint string +} + +func (a *BeintooAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errors []error + + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("No Imps in Bid Request"), + }} + } + + if errors := preprocess(request); errors != nil && len(errors) > 0 { + return nil, append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Error in preprocess of Imp, err: %s", errors), + }) + } + + data, err := json.Marshal(request) + if err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Error in packaging request to JSON"), + }} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + if request.Device != nil { + addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA) + addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP) + addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) + if request.Device.DNT != nil { + addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) + } + } + if request.Site != nil { + addHeaderIfNonEmpty(headers, "Referer", request.Site.Page) + } + + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.endpoint, + Body: data, + Headers: headers, + }}, errors +} + +func unpackImpExt(imp *openrtb.Imp) (*openrtb_ext.ExtImpBeintoo, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + + var beintooExt openrtb_ext.ExtImpBeintoo + if err := json.Unmarshal(bidderExt.Bidder, &beintooExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, invalid ImpExt", imp.ID), + } + } + + tagIDValidation, err := strconv.ParseInt(beintooExt.TagID, 10, 64) + if err != nil || tagIDValidation == 0 { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, invalid tagid must be a String of numbers", imp.ID), + } + } + + return &beintooExt, nil +} + +func buildImpBanner(imp *openrtb.Imp) error { + imp.Ext = nil + + if imp.Banner == nil { + return &errortypes.BadInput{ + Message: fmt.Sprintf("Request needs to include a Banner object"), + } + } + + bannerCopy := *imp.Banner + banner := &bannerCopy + + if banner.W == nil && banner.H == nil { + if len(banner.Format) == 0 { + return &errortypes.BadInput{ + Message: fmt.Sprintf("Need at least one size to build request"), + } + } + format := banner.Format[0] + banner.Format = banner.Format[1:] + banner.W = &format.W + banner.H = &format.H + imp.Banner = banner + } + + return nil +} + +// Add Beintoo required properties to Imp object +func addImpProps(imp *openrtb.Imp, secure *int8, BeintooExt *openrtb_ext.ExtImpBeintoo) { + imp.TagID = BeintooExt.TagID + imp.Secure = secure + + if BeintooExt.BidFloor != "" { + bidFloor, err := strconv.ParseFloat(BeintooExt.BidFloor, 64) + if err != nil { + bidFloor = 0 + } + + if bidFloor > 0 { + imp.BidFloor = bidFloor + } + } + + return +} + +// Adding header fields to request header +func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { + if len(headerValue) > 0 { + headers.Add(headerName, headerValue) + } +} + +// Handle request errors and formatting to be sent to Beintoo +func preprocess(request *openrtb.BidRequest) []error { + errors := make([]error, 0, len(request.Imp)) + resImps := make([]openrtb.Imp, 0, len(request.Imp)) + secure := int8(0) + + if request.Site != nil && request.Site.Page != "" { + pageURL, err := url.Parse(request.Site.Page) + if err == nil && pageURL.Scheme == "https" { + secure = int8(1) + } + } + + for _, imp := range request.Imp { + beintooExt, err := unpackImpExt(&imp) + if err != nil { + errors = append(errors, err) + return errors + } + + addImpProps(&imp, &secure, beintooExt) + + if err := buildImpBanner(&imp); err != nil { + errors = append(errors, err) + return errors + } + resImps = append(resImps, imp) + } + + request.Imp = resImps + + return errors +} + +// MakeBids make the bids for the bid response. +func (a *BeintooAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + if response.StatusCode == http.StatusNoContent { + // no bid response + return nil, nil + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Invalid Status Returned: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unable to unpackage bid response. Error: %s", err.Error()), + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + sb.Bid[i].ImpID = sb.Bid[i].ID + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: "banner", + }) + } + } + + return bidResponse, nil + +} + +func NewBeintooBidder(endpoint string) *BeintooAdapter { + return &BeintooAdapter{ + endpoint: endpoint, + } +} diff --git a/adapters/beintoo/beintoo_test.go b/adapters/beintoo/beintoo_test.go new file mode 100644 index 00000000000..863da1513e5 --- /dev/null +++ b/adapters/beintoo/beintoo_test.go @@ -0,0 +1,12 @@ +package beintoo + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + beintooAdapter := NewBeintooBidder("https://ib.beintoo.com") + adapterstest.RunJSONBidderTest(t, "beintootest", beintooAdapter) +} diff --git a/adapters/beintoo/beintootest/exemplary/minimal-banner.json b/adapters/beintoo/beintootest/exemplary/minimal-banner.json new file mode 100644 index 00000000000..60e481c507c --- /dev/null +++ b/adapters/beintoo/beintootest/exemplary/minimal-banner.json @@ -0,0 +1,117 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + } + }], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://ib.beintoo.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Referer": [ + "http://www.publisher.com/awesome/site?with=some¶meters=here" + ], + "Dnt": [ + "1" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36" + ] + }, + "body": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "tagid": "25251", + "secure": 0 + }], + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + }, + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [{ + "seat": "12356", + "bid": [{ + "adm": "
" +const adSourceURL = "https://ad.yieldlab.net/d/%v/%v/%v?%v" +const creativeID = "%v%v%v" diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go new file mode 100644 index 00000000000..8c230c15b15 --- /dev/null +++ b/adapters/yieldlab/params_test.go @@ -0,0 +1,63 @@ +package yieldlab + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/yieldlab.json +// +// These also validate the format of the external API: request.imp[i].ext.yieldlab + +// TestValidParams makes sure that the yieldlab schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderYieldlab, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected yieldlab params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the yieldlab schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderYieldlab, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"adslotId": "123","supplyId":"23456","adSize":"100x100"}`, + `{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf"}`, + `{"adslotId": "123","supplyId":"23456","adSize":"100x100","extId":"asdf","targeting":{"a":"b"}}`, + `{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`, + `{"adslotId": "123","supplyId":"23456","adSize":"100x100","targeting":{"a":"b"}}`, +} + +var invalidParams = []string{ + `{"supplyId":"23456","adSize":"100x100"}`, + `{"adslotId": "123","adSize":"100x100","extId":"asdf"}`, + `{"adslotId": "123","supplyId":"23456","extId":"asdf","targeting":{"a":"b"}}`, + `{"adslotId": "123","supplyId":"23456"}`, + `{"adSize":"100x100","supplyId":"23456"}`, + `{"adslotId": "123","adSize":"100x100"}`, + `{"supplyId":"23456"}`, + `{"adslotId": "123"}`, + `{}`, + `[]`, + `{"a":"b"}`, + `null`, +} diff --git a/adapters/yieldlab/types.go b/adapters/yieldlab/types.go new file mode 100644 index 00000000000..90612700713 --- /dev/null +++ b/adapters/yieldlab/types.go @@ -0,0 +1,29 @@ +package yieldlab + +import ( + "strconv" + "time" +) + +type bidResponse struct { + ID uint64 `json:"id"` + Price uint `json:"price"` + Advertiser string `json:"advertiser"` + Adsize string `json:"adsize"` + Pid uint64 `json:"pid"` + Did uint64 `json:"did"` + Pvid string `json:"pvid"` +} + +type cacheBuster func() string + +type weekGenerator func() string + +var defaultCacheBuster cacheBuster = func() string { + return strconv.FormatInt(time.Now().Unix(), 10) +} + +var defaultWeekGenerator weekGenerator = func() string { + _, week := time.Now().ISOWeek() + return strconv.Itoa(week) +} diff --git a/adapters/yieldlab/usersync.go b/adapters/yieldlab/usersync.go new file mode 100644 index 00000000000..3ee9a3fdfb5 --- /dev/null +++ b/adapters/yieldlab/usersync.go @@ -0,0 +1,12 @@ +package yieldlab + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewYieldlabSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("yieldlab", 70, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/yieldlab/usersync_test.go b/adapters/yieldlab/usersync_test.go new file mode 100644 index 00000000000..3892c16bf05 --- /dev/null +++ b/adapters/yieldlab/usersync_test.go @@ -0,0 +1,26 @@ +package yieldlab + +import ( + "testing" + "text/template" + + "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" +) + +func TestYieldlabSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25YL_UID%25%25")) + syncer := NewYieldlabSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + }, + }) + assert.NoError(t, err) + assert.Equal(t, "https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr=0&gdpr_consent=&redirectUri=http%3A%2F%2Flocalhost%2F%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%25%25YL_UID%25%25", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 70, syncer.GDPRVendorID()) + assert.False(t, syncInfo.SupportCORS) +} diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go new file mode 100644 index 00000000000..20f3674797d --- /dev/null +++ b/adapters/yieldlab/yieldlab.go @@ -0,0 +1,314 @@ +package yieldlab + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strconv" + "strings" + + "github.com/mxmCherry/openrtb" + "golang.org/x/text/currency" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// YieldlabAdapter connects the Yieldlab API to prebid server +type YieldlabAdapter struct { + endpoint string + cacheBuster cacheBuster + getWeek weekGenerator +} + +// NewYieldlabBidder returns a new YieldlabBidder instance +func NewYieldlabBidder(endpoint string) *YieldlabAdapter { + return &YieldlabAdapter{ + endpoint: endpoint, + cacheBuster: defaultCacheBuster, + getWeek: defaultWeekGenerator, + } +} + +// Builds endpoint url based on adapter-specific pub settings from imp.ext +func (a *YieldlabAdapter) makeEndpointURL(req *openrtb.BidRequest, params *openrtb_ext.ExtImpYieldlab) (string, error) { + uri, err := url.Parse(a.endpoint) + if err != nil { + return "", fmt.Errorf("failed to parse yieldlab endpoint: %v", err) + } + + uri.Path = path.Join(uri.Path, params.AdslotID) + q := uri.Query() + q.Set("content", "json") + q.Set("pvid", "true") + q.Set("ts", a.cacheBuster()) + q.Set("t", a.makeTargetingValues(params)) + + if req.User != nil && req.User.BuyerUID != "" { + q.Set("ids", "ylid:"+req.User.BuyerUID) + } + + if req.Device != nil { + q.Set("yl_rtb_ifa", req.Device.IFA) + q.Set("yl_rtb_devicetype", fmt.Sprintf("%v", req.Device.DeviceType)) + + if req.Device.ConnectionType != nil { + q.Set("yl_rtb_connectiontype", fmt.Sprintf("%v", req.Device.ConnectionType.Val())) + } + + if req.Device.Geo != nil { + q.Set("lat", fmt.Sprintf("%v", req.Device.Geo.Lat)) + q.Set("lon", fmt.Sprintf("%v", req.Device.Geo.Lon)) + } + } + + if req.App != nil { + q.Set("pubappname", req.App.Name) + q.Set("pubbundlename", req.App.Bundle) + } + + gdpr, consent, err := a.getGDPR(req) + if err != nil { + return "", err + } + if gdpr != "" && consent != "" { + q.Set("gdpr", gdpr) + q.Set("consent", consent) + } + + uri.RawQuery = q.Encode() + + return uri.String(), nil +} + +func (a *YieldlabAdapter) getGDPR(request *openrtb.BidRequest) (string, string, error) { + gdpr := "" + var extRegs openrtb_ext.ExtRegs + if request.Regs != nil { + if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil { + return "", "", fmt.Errorf("failed to parse ExtRegs in Yieldlab GDPR check: %v", err) + } + if extRegs.GDPR != nil && (*extRegs.GDPR == 0 || *extRegs.GDPR == 1) { + gdpr = strconv.Itoa(int(*extRegs.GDPR)) + } + } + + consent := "" + if request.User != nil && request.User.Ext != nil { + var extUser openrtb_ext.ExtUser + if err := json.Unmarshal(request.User.Ext, &extUser); err != nil { + return "", "", fmt.Errorf("failed to parse ExtUser in Yieldlab GDPR check: %v", err) + } + consent = extUser.Consent + } + + return gdpr, consent, nil +} + +func (a *YieldlabAdapter) makeTargetingValues(params *openrtb_ext.ExtImpYieldlab) string { + values := url.Values{} + for k, v := range params.Targeting { + values.Set(k, v) + } + return values.Encode() +} + +func (a *YieldlabAdapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{fmt.Errorf("invalid request %+v, no Impressions given", request)} + } + + bidURL, err := a.makeEndpointURL(request, a.mergeParams(a.parseRequest(request))) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Accept", "application/json") + if request.Site != nil { + headers.Add("Referer", request.Site.Page) + } + if request.Device != nil { + headers.Add("User-Agent", request.Device.UA) + headers.Add("X-Forwarded-For", request.Device.IP) + } + if request.User != nil { + headers.Add("Cookie", "id="+request.User.BuyerUID) + } + + return []*adapters.RequestData{{ + Method: "GET", + Uri: bidURL, + Headers: headers, + }}, nil +} + +// parseRequest extracts the Yieldlab request information from the request +func (a *YieldlabAdapter) parseRequest(request *openrtb.BidRequest) []*openrtb_ext.ExtImpYieldlab { + params := make([]*openrtb_ext.ExtImpYieldlab, 0) + + for i := 0; i < len(request.Imp); i++ { + bidderExt := new(adapters.ExtImpBidder) + if err := json.Unmarshal(request.Imp[i].Ext, bidderExt); err != nil { + continue + } + + yieldlabExt := new(openrtb_ext.ExtImpYieldlab) + if err := json.Unmarshal(bidderExt.Bidder, yieldlabExt); err != nil { + continue + } + + params = append(params, yieldlabExt) + } + + return params +} + +func (a *YieldlabAdapter) mergeParams(params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab { + var adSlotIds []string + targeting := make(map[string]string) + + for _, p := range params { + adSlotIds = append(adSlotIds, p.AdslotID) + for k, v := range p.Targeting { + targeting[k] = v + } + } + + return &openrtb_ext.ExtImpYieldlab{ + AdslotID: strings.Join(adSlotIds, adSlotIdSeparator), + Targeting: targeting, + } +} + +// MakeBids make the bids for the bid response. +func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode != 200 { + return nil, []error{ + &errortypes.BadServerResponse{ + Message: fmt.Sprintf("failed to resolve bids from yieldlab response: Unexpected response code %v", response.StatusCode), + }, + } + } + + bids := make([]*bidResponse, 0) + if err := json.Unmarshal(response.Body, &bids); err != nil { + return nil, []error{ + &errortypes.BadServerResponse{ + Message: fmt.Sprintf("failed to parse bids response from yieldlab: %v", err), + }, + } + } + + params := a.parseRequest(internalRequest) + + bidderResponse := &adapters.BidderResponse{ + Currency: currency.EUR.String(), + Bids: []*adapters.TypedBid{}, + } + + for i, bid := range bids { + width, height, err := splitSize(bid.Adsize) + if err != nil { + return nil, []error{err} + } + + req := a.findBidReq(bid.ID, params) + if req == nil { + return nil, []error{ + fmt.Errorf("failed to find yieldlab request for adslotID %v. This is most likely a programming issue", bid.ID), + } + } + + var bidType openrtb_ext.BidType + responseBid := &openrtb.Bid{ + ID: strconv.FormatUint(bid.ID, 10), + Price: float64(bid.Price) / 100, + ImpID: internalRequest.Imp[i].ID, + CrID: a.makeCreativeID(req, bid), + DealID: strconv.FormatUint(bid.Pid, 10), + W: width, + H: height, + } + + if internalRequest.Imp[i].Video != nil { + bidType = openrtb_ext.BidTypeVideo + responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid) + + } else if internalRequest.Imp[i].Banner != nil { + bidType = openrtb_ext.BidTypeBanner + responseBid.AdM = a.makeBannerAdSource(internalRequest, req, bid) + } else { + // Yieldlab adapter currently doesn't support Audio and Native ads + continue + } + + bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{ + BidType: bidType, + Bid: responseBid, + }) + } + + return bidderResponse, nil +} + +func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab { + slotIdStr := strconv.FormatUint(adslotID, 10) + for _, p := range params { + if p.AdslotID == slotIdStr { + return p + } + } + + return nil +} + +func (a *YieldlabAdapter) makeBannerAdSource(req *openrtb.BidRequest, ext *openrtb_ext.ExtImpYieldlab, res *bidResponse) string { + return fmt.Sprintf(adSourceBanner, a.makeAdSourceURL(req, ext, res)) +} + +func (a *YieldlabAdapter) makeAdSourceURL(req *openrtb.BidRequest, ext *openrtb_ext.ExtImpYieldlab, res *bidResponse) string { + val := url.Values{} + val.Set("ts", a.cacheBuster()) + val.Set("id", ext.ExtId) + val.Set("pvid", res.Pvid) + + if req.User != nil { + val.Set("ids", "ylid:"+req.User.BuyerUID) + } + + gdpr, consent, err := a.getGDPR(req) + if err == nil && gdpr != "" && consent != "" { + val.Set("gdpr", gdpr) + val.Set("consent", consent) + } + + return fmt.Sprintf(adSourceURL, ext.AdslotID, ext.SupplyID, res.Adsize, val.Encode()) +} + +func (a *YieldlabAdapter) makeCreativeID(req *openrtb_ext.ExtImpYieldlab, bid *bidResponse) string { + return fmt.Sprintf(creativeID, req.AdslotID, bid.Pid, a.getWeek()) +} + +func splitSize(size string) (uint64, uint64, error) { + sizeParts := strings.Split(size, adsizeSeparator) + if len(sizeParts) != 2 { + return 0, 0, nil + } + + width, err := strconv.ParseUint(sizeParts[0], 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse yieldlab adsize: %v", err) + } + + height, err := strconv.ParseUint(sizeParts[1], 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse yieldlab adsize: %v", err) + } + + return width, height, nil + +} diff --git a/adapters/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go new file mode 100644 index 00000000000..b6ca0507ab8 --- /dev/null +++ b/adapters/yieldlab/yieldlab_test.go @@ -0,0 +1,128 @@ +package yieldlab + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +const testURL = "https://ad.yieldlab.net/testing/" + +var testCacheBuster cacheBuster = func() string { + return "testing" +} + +var testWeekGenerator weekGenerator = func() string { + return "33" +} + +func newTestYieldlabBidder(endpoint string) *YieldlabAdapter { + return &YieldlabAdapter{ + endpoint: endpoint, + cacheBuster: testCacheBuster, + getWeek: testWeekGenerator, + } +} + +func TestNewYieldlabBidder(t *testing.T) { + bid := NewYieldlabBidder(testURL) + assert.NotNil(t, bid) + assert.Equal(t, bid.endpoint, testURL) + assert.NotNil(t, bid.cacheBuster) + assert.NotNil(t, bid.getWeek) +} + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "yieldlabtest", newTestYieldlabBidder(testURL)) +} + +func Test_splitSize(t *testing.T) { + type args struct { + size string + } + tests := []struct { + name string + args args + want uint64 + want1 uint64 + wantErr bool + }{ + { + name: "valid", + args: args{ + size: "300x800", + }, + want: 300, + want1: 800, + wantErr: false, + }, + { + name: "empty", + args: args{ + size: "", + }, + want: 0, + want1: 0, + wantErr: false, + }, + { + name: "invalid", + args: args{ + size: "test", + }, + want: 0, + want1: 0, + wantErr: false, + }, + { + name: "invalid_height", + args: args{ + size: "200xtest", + }, + want: 0, + want1: 0, + wantErr: true, + }, + { + name: "invalid_width", + args: args{ + size: "testx200", + }, + want: 0, + want1: 0, + wantErr: true, + }, + { + name: "invalid_separator", + args: args{ + size: "200y200", + }, + want: 0, + want1: 0, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, err := splitSize(tt.args.size) + if (err != nil) != tt.wantErr { + t.Errorf("splitSize() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("splitSize() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("splitSize() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestYieldlabAdapter_makeEndpointURL_invalidEndpoint(t *testing.T) { + bid := NewYieldlabBidder("test$:/something§") + _, err := bid.makeEndpointURL(nil, nil) + assert.Error(t, err) +} diff --git a/adapters/yieldlab/yieldlabtest/exemplary/banner.json b/adapters/yieldlab/yieldlabtest/exemplary/banner.json new file mode 100644 index 00000000000..8dd94404097 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/exemplary/banner.json @@ -0,0 +1,111 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "adSize": "728x90", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c" + }, + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "adm": "", + "crid": "12345123433", + "dealid": "1234", + "id": "12345", + "impid": "test-imp-id", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json new file mode 100644 index 00000000000..381ba688e09 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/exemplary/gdpr.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "adSize": "728x90", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "regs": { + "ext": { + "gdpr": 1 + } + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + }, + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c", + "ext": { + "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "adm": "", + "crid": "12345123433", + "dealid": "1234", + "id": "12345", + "impid": "test-imp-id", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video.json b/adapters/yieldlab/yieldlabtest/exemplary/video.json new file mode 100644 index 00000000000..9e970ae79b5 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/exemplary/video.json @@ -0,0 +1,136 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "adSize": "728x90", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + }, + "video": { + "context": "instream", + "mimes": [ + "video/mp4" + ], + "playerSize": [ + [ + 400, + 600 + ] + ], + "minduration": 1, + "maxduration": 2, + "protocols": [ + 1, + 2 + ], + "w": 1, + "h": 2, + "startdelay": 1, + "placement": 1, + "playbackmethod": [ + 2 + ] + } + } + ], + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c" + }, + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "crid": "12345123433", + "dealid": "1234", + "id": "12345", + "impid": "test-imp-id", + "nurl": "https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/exemplary/video_app.json b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json new file mode 100644 index 00000000000..67d526b3400 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/exemplary/video_app.json @@ -0,0 +1,136 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "adSize": "728x90", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + }, + "video": { + "context": "instream", + "mimes": [ + "video/mp4" + ], + "playerSize": [ + [ + 400, + 600 + ] + ], + "minduration": 1, + "maxduration": 2, + "protocols": [ + 1, + 2 + ], + "w": 1, + "h": 2, + "startdelay": 1, + "placement": 1, + "playbackmethod": [ + 2 + ] + } + } + ], + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?content=json&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pubappname=Awesome+App&pubbundlename=com.app.awesome&pvid=true&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "crid": "12345123433", + "dealid": "1234", + "id": "12345", + "impid": "test-imp-id", + "nurl": "https://ad.yieldlab.net/d/12345/123456789/728x90?id=abc&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&pvid=40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5&ts=testing", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "video" + } + ] + } + ] +} diff --git a/config/config.go b/config/config.go index 5c66f4cdf02..86f2e9a98a0 100755 --- a/config/config.go +++ b/config/config.go @@ -551,6 +551,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderValueImpression, "https://rtb.valueimpression.com/usersync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvalueimpression%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldlab, "https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25YL_UID%25%25") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldone, "https://y.one.impact-ad.jp/hbs_sc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderZeroClickFraud, "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") @@ -760,6 +761,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") v.SetDefault("adapters.vrtcal.endpoint", "http://rtb.vrtcal.com/bidder_prebid.vap?ssp=1804") v.SetDefault("adapters.yeahmobi.endpoint", "https://{{.Host}}/prebid/bid") + v.SetDefault("adapters.yieldlab.endpoint", "https://ad.yieldlab.net/yp/") v.SetDefault("adapters.yieldmo.endpoint", "https://ads.yieldmo.com/exchange/prebid-server") v.SetDefault("adapters.yieldone.endpoint", "https://y.one.impact-ad.jp/hbs_imp") v.SetDefault("adapters.zeroclickfraud.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index b91f01a7e9a..b69b5b50e13 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -73,6 +73,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/yeahmobi" + "github.com/prebid/prebid-server/adapters/yieldlab" "github.com/prebid/prebid-server/adapters/yieldmo" "github.com/prebid/prebid-server/adapters/yieldone" "github.com/prebid/prebid-server/adapters/zeroclickfraud" @@ -153,6 +154,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderUcfunnel: ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint), openrtb_ext.BidderUnruly: unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint), openrtb_ext.BidderValueImpression: valueimpression.NewValueImpressionBidder(cfg.Adapters[string(openrtb_ext.BidderValueImpression)].Endpoint), + openrtb_ext.BidderYieldlab: yieldlab.NewYieldlabBidder(cfg.Adapters[string(openrtb_ext.BidderYieldlab)].Endpoint), openrtb_ext.BidderVerizonMedia: verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint), openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), openrtb_ext.BidderVrtcal: vrtcal.NewVrtcalBidder(cfg.Adapters[string(openrtb_ext.BidderVrtcal)].Endpoint), diff --git a/go.mod b/go.mod index 89cc69e4519..8de6f10e4b9 100644 --- a/go.mod +++ b/go.mod @@ -7,23 +7,17 @@ require ( github.com/DATA-DOG/go-sqlmock v1.3.0 github.com/NYTimes/gziphandler v1.1.1 github.com/OneOfOne/xxhash v1.2.5 // indirect - github.com/aerospike/aerospike-client-go v2.7.2+incompatible github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/blang/semver v3.5.1+incompatible - github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 github.com/cespare/xxhash v1.0.0 // indirect github.com/chasex/glog v0.0.0-20160217080310-c62392af379c github.com/coocood/freecache v1.0.1 - github.com/didip/tollbooth v4.0.2+incompatible github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd - github.com/go-redis/redis v6.15.7+incompatible - github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 github.com/gofrs/uuid v3.2.0+incompatible github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/golang/snappy v0.0.1 github.com/hashicorp/hcl v1.0.0 // indirect github.com/influxdata/influxdb v1.6.1 // indirect github.com/julienschmidt/httprouter v1.1.0 @@ -37,10 +31,8 @@ require ( github.com/mxmCherry/openrtb v11.0.0+incompatible github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.2.0 // indirect github.com/prebid/go-gdpr v0.7.0 - github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect @@ -48,27 +40,24 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 github.com/rs/cors v1.5.0 github.com/sergi/go-diff v1.0.0 // indirect - github.com/sirupsen/logrus v1.4.2 github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.1.1 // indirect github.com/spf13/cast v1.2.0 // indirect github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect github.com/spf13/pflag v1.0.2 // indirect github.com/spf13/viper v1.1.0 + github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.5.1 - github.com/valyala/fasthttp v1.9.0 github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 - github.com/xorcare/pointer v1.1.0 github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect github.com/yudai/pp v2.0.1+incompatible // indirect - github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect + golang.org/x/sys v0.0.0-20190422165155-953cdadca894 // indirect golang.org/x/text v0.3.0 - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index f929408f0f3..176bacfc20a 100644 --- a/go.sum +++ b/go.sum @@ -6,57 +6,35 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/aerospike/aerospike-client-go v2.7.2+incompatible h1:bWbRf8trg1FhKF7u43KLGNfOH60RlvIgQjpaS107DZ8= -github.com/aerospike/aerospike-client-go v2.7.2+incompatible/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= -github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.0.0 h1:naDmySfoNg0nKS62/ujM6e71ZgM2AoVdaqGwMG0w18A= github.com/cespare/xxhash v1.0.0/go.mod h1:fX/lfQBkSCDXZSUgv6jVIu/EVA3/JNseAX5asI4c4T4= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c h1:eXqCBUHfmjbeDqcuvzjsd+bM6A+bnwo5N9FVbV6m5/s= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c/go.mod h1:omJZNg0Qu76bxJd+ExohVo8uXzNcGOk2bv7vel460xk= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/coocood/freecache v1.0.1 h1:oFyo4msX2c0QIKU+kuMJUwsKamJ+AKc2JJrKcMszJ5M= github.com/coocood/freecache v1.0.1/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M= -github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd h1:biTJQdqouE5by89AAffXG8++TY+9Fsdrg5rinbt3tHk= github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U= -github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= -github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5 h1:ZZVxQRCm4ewuoqqLBJ7LHpsk4OGx2PkyCsRKLq4oHgE= -github.com/gocql/gocql v0.0.0-20200203083758-81b8263d9fe5/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -67,17 +45,6 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -99,16 +66,12 @@ github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prebid/go-gdpr v0.7.0 h1:m4E/FjUhTBMciDsd3lQlbzFyXLzNK+JQkFmInJpFAwc= github.com/prebid/go-gdpr v0.7.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= -github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= -github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= @@ -123,8 +86,6 @@ github.com/rs/cors v1.5.0 h1:dgSHE6+ia18arGOTIYQKKGWLvEbGvmbNE6NfxhoNHUY= github.com/rs/cors v1.5.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= @@ -140,16 +101,10 @@ github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7Sr github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw= -github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303 h1:Va10CytCCYRm4xBTses5ZDeDjeIQjhaiC9nRCe/yflI= github.com/vrischmann/go-metrics-influxdb v0.0.0-20160917065939-43af8332c303/go.mod h1:Xdcad1nGVhQfhoV0go+/4WaI/RZkWlvfjkVCdpMTxPY= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= @@ -158,16 +113,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609 h1:BcMExZAULPkihVZ7UJXK7t8rwGqisXFw75tILnafhBY= github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xorcare/pointer v1.1.0 h1:sFwXOhRF8QZ0tyVZrtxWGIoVZNEmRzBCaFWdONPQIUM= -github.com/xorcare/pointer v1.1.0/go.mod h1:6KLhkOh6YbuvZkT4YbxIbR/wzLBjyMxOiNzZhJTor2Y= github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d h1:yJIizrfO599ot2kQ6Af1enICnwBD3XoxgX3MrMwot2M= github.com/yudai/gojsondiff v0.0.0-20170107030110-7b1b7adf999d/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= -github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -178,7 +129,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -186,14 +136,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/p golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index f2f8e7c67ab..8a53e4adcf2 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -91,6 +91,7 @@ const ( BidderVisx BidderName = "visx" BidderVrtcal BidderName = "vrtcal" BidderYeahmobi BidderName = "yeahmobi" + BidderYieldlab BidderName = "yieldlab" BidderYieldmo BidderName = "yieldmo" BidderYieldone BidderName = "yieldone" BidderZeroClickFraud BidderName = "zeroclickfraud" @@ -166,6 +167,7 @@ var BidderMap = map[string]BidderName{ "visx": BidderVisx, "vrtcal": BidderVrtcal, "yeahmobi": BidderYeahmobi, + "yieldlab": BidderYieldlab, "yieldmo": BidderYieldmo, "yieldone": BidderYieldone, "zeroclickfraud": BidderZeroClickFraud, diff --git a/openrtb_ext/imp_yieldlab.go b/openrtb_ext/imp_yieldlab.go new file mode 100644 index 00000000000..604b7e8ceab --- /dev/null +++ b/openrtb_ext/imp_yieldlab.go @@ -0,0 +1,10 @@ +package openrtb_ext + +// ExtImpYieldlab defines the contract for bidrequest.imp[i].ext.yieldlab +type ExtImpYieldlab struct { + AdslotID string `json:"adslotId"` + SupplyID string `json:"supplyId"` + AdSize string `json:"adSize"` + Targeting map[string]string `json:"targeting"` + ExtId string `json:"extId"` +} diff --git a/static/bidder-info/yieldlab.yaml b/static/bidder-info/yieldlab.yaml new file mode 100644 index 00000000000..654e6c749cb --- /dev/null +++ b/static/bidder-info/yieldlab.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "solutions@yieldlab.de" +capabilities: + site: + mediaTypes: + - banner + - video + app: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/yieldlab.json b/static/bidder-params/yieldlab.json new file mode 100644 index 00000000000..900d65da6e5 --- /dev/null +++ b/static/bidder-params/yieldlab.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Yieldlab Adapter Params", + "description": "A schema which validates params accepted by the Yieldlab adapter", + "type": "object", + "properties": { + "adslotId": { + "type": "string", + "description": "Yieldlab ID of the ad slot" + }, + "supplyId": { + "type": "string", + "description": "Yieldlab ID of the supply" + }, + "adSize": { + "type": "string", + "description": "Size of the adslot in pixel, e.g. 200x50" + }, + "extId": { + "type": "string", + "description": "External ID used for reporting" + }, + "targeting": { + "type": "object", + "description": "Targeting information in key value pairs" + } + }, + "required": [ + "adslotId", + "supplyId", + "adSize" + ] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 3f12ee7f728..791a00de0a9 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -61,6 +61,7 @@ import ( "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" + "github.com/prebid/prebid-server/adapters/yieldlab" "github.com/prebid/prebid-server/adapters/yieldmo" "github.com/prebid/prebid-server/adapters/yieldone" "github.com/prebid/prebid-server/adapters/zeroclickfraud" @@ -131,6 +132,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVrtcal, vrtcal.NewVrtcalSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldlab, yieldlab.NewYieldlabSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldmo, yieldmo.NewYieldmoSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderYieldone, yieldone.NewYieldoneSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderZeroClickFraud, zeroclickfraud.NewZeroClickFraudSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 20fce80c83a..9aae284da2a 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -67,6 +67,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderUcfunnel): syncConfig, string(openrtb_ext.BidderUnruly): syncConfig, string(openrtb_ext.BidderValueImpression): syncConfig, + string(openrtb_ext.BidderYieldlab): syncConfig, string(openrtb_ext.BidderVerizonMedia): syncConfig, string(openrtb_ext.BidderVisx): syncConfig, string(openrtb_ext.BidderVrtcal): syncConfig, From d29a749f6835ef521a6ddfaa2818ef4fa66aabf0 Mon Sep 17 00:00:00 2001 From: Gena Date: Tue, 2 Jun 2020 21:59:01 +0300 Subject: [PATCH 101/318] Update adtelligent ortb endpoint (#1318) --- adapters/adtelligent/adtelligent.go | 1 - adapters/adtelligent/adtelligent_test.go | 2 +- .../adtelligenttest/exemplary/media-type-mapping.json | 2 +- .../adtelligent/adtelligenttest/exemplary/simple-banner.json | 2 +- .../adtelligent/adtelligenttest/exemplary/simple-video.json | 2 +- .../adtelligenttest/supplemental/explicit-dimensions.json | 2 +- .../supplemental/wrong-impression-mapping.json | 2 +- config/config.go | 4 ++-- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/adapters/adtelligent/adtelligent.go b/adapters/adtelligent/adtelligent.go index 7f0262fdc92..78a71dcf9cd 100644 --- a/adapters/adtelligent/adtelligent.go +++ b/adapters/adtelligent/adtelligent.go @@ -55,7 +55,6 @@ func (a *AdtelligentAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo * imps := request.Imp request.Imp = make([]openrtb.Imp, 0, len(imps)) - for sourceId, impIds := range imp2source { request.Imp = request.Imp[:0] diff --git a/adapters/adtelligent/adtelligent_test.go b/adapters/adtelligent/adtelligent_test.go index 63655da677e..b8894c5e4d9 100644 --- a/adapters/adtelligent/adtelligent_test.go +++ b/adapters/adtelligent/adtelligent_test.go @@ -7,5 +7,5 @@ import ( ) func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "adtelligenttest", NewAdtelligentBidder("http://hb.adtelligent.com/auction")) + adapterstest.RunJSONBidderTest(t, "adtelligenttest", NewAdtelligentBidder("http://ghb.adtelligent.com/pbs/ortb")) } diff --git a/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json b/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json index 67ad2fd2915..553ec61833b 100644 --- a/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json +++ b/adapters/adtelligent/adtelligenttest/exemplary/media-type-mapping.json @@ -24,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://hb.adtelligent.com/auction?aid=1000", + "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json b/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json index 6648229de95..a06477b4d18 100644 --- a/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json +++ b/adapters/adtelligent/adtelligenttest/exemplary/simple-banner.json @@ -30,7 +30,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://hb.adtelligent.com/auction?aid=1000", + "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json b/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json index 97769651997..f108cc94b17 100644 --- a/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json +++ b/adapters/adtelligent/adtelligenttest/exemplary/simple-video.json @@ -24,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://hb.adtelligent.com/auction?aid=1000", + "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json b/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json index 9dc279bcd1c..6155e9bc56b 100644 --- a/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json +++ b/adapters/adtelligent/adtelligenttest/supplemental/explicit-dimensions.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://hb.adtelligent.com/auction?aid=1000", + "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json b/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json index 94df34af40d..2e5aeff311f 100644 --- a/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json +++ b/adapters/adtelligent/adtelligenttest/supplemental/wrong-impression-mapping.json @@ -24,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://hb.adtelligent.com/auction?aid=1000", + "uri": "http://ghb.adtelligent.com/pbs/ortb?aid=1000", "body": { "id": "test-request-id", "imp": [ diff --git a/config/config.go b/config/config.go index 86f2e9a98a0..3b34d3a4815 100755 --- a/config/config.go +++ b/config/config.go @@ -500,7 +500,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernel, "https://sync.adkernel.com/user-sync?t=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadkernel%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernelAdn, "https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") // openrtb_ext.BidderAdOcean doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -702,7 +702,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") - v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") + v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb") v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.aja.endpoint", "https://ad.as.amanad.adtdp.com/v1/bid/4") v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid") From b5993cd0e5912638b03cea039466673325dee40b Mon Sep 17 00:00:00 2001 From: Seba Perez Date: Tue, 2 Jun 2020 16:05:03 -0300 Subject: [PATCH 102/318] Change on eplanning endpoint (#1327) --- adapters/eplanning/eplanning.go | 1 - adapters/eplanning/eplanning_test.go | 2 +- adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json | 2 +- adapters/eplanning/eplanningtest/exemplary/simple-banner.json | 2 +- adapters/eplanning/eplanningtest/exemplary/two-banners.json | 2 +- .../supplemental/app-domain-and-url-correctly-parsed.json | 2 +- .../eplanningtest/supplemental/banner-no-size-sends-1x1.json | 2 +- .../eplanningtest/supplemental/invalid-response-no-bids.json | 2 +- .../supplemental/invalid-response-unmarshall-error.json | 2 +- .../eplanningtest/supplemental/server-bad-request.json | 2 +- .../eplanning/eplanningtest/supplemental/server-error-code.json | 2 +- .../eplanning/eplanningtest/supplemental/server-no-content.json | 2 +- .../supplemental/site-domain-and-url-correctly-parsed.json | 2 +- config/config.go | 2 +- 14 files changed, 13 insertions(+), 14 deletions(-) diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go index 5fb9ccf27c2..2a46b5469e0 100644 --- a/adapters/eplanning/eplanning.go +++ b/adapters/eplanning/eplanning.go @@ -133,7 +133,6 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn uriObj.Path = uriObj.Path + fmt.Sprintf("/%s/%s/%s/%s", clientID, dfpClientID, requestTarget, sec) query := url.Values{} - query.Set("r", "pbs") query.Set("ncb", "1") if request.App == nil { query.Set("ur", pageURL) diff --git a/adapters/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go index d2c331d456d..461fb849ced 100644 --- a/adapters/eplanning/eplanning_test.go +++ b/adapters/eplanning/eplanning_test.go @@ -8,7 +8,7 @@ import ( ) func TestJsonSamples(t *testing.T) { - eplanningAdapter := NewEPlanningBidder(new(http.Client), "https://ads.us.e-planning.net/hb/1") + eplanningAdapter := NewEPlanningBidder(new(http.Client), "https://ads.us.e-planning.net/pbs/1") eplanningAdapter.testing = true adapterstest.RunJSONBidderTest(t, "eplanningtest", eplanningAdapter) } diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json index e602877f27f..a67a86d18e6 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json @@ -28,7 +28,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json index 0403e59a763..9146bb4afb5 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json @@ -31,7 +31,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&r=pbs&uid=2154987&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&uid=2154987&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/two-banners.json b/adapters/eplanning/eplanningtest/exemplary/two-banners.json index 8c4ca0214db..174c8ce3fc6 100644 --- a/adapters/eplanning/eplanningtest/exemplary/two-banners.json +++ b/adapters/eplanning/eplanningtest/exemplary/two-banners.json @@ -39,7 +39,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json index e6e25566b6a..04df82f6668 100644 --- a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json +++ b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json @@ -39,7 +39,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1&r=pbs", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json index f1bc29e1afc..f02eb80fe41 100644 --- a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json +++ b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json index 978989e295f..8bdcfddd733 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json index 7198f4ee117..9f5b2d7fc03 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json index 938ede62664..2ef03648884 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json index eaa2a677f93..76e75a5c203 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json index d1feb865f0d..02f1fa46d33 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=FILE", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json index 2f43b9aac2f..581cb1d5b46 100644 --- a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json +++ b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/hb/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&r=pbs&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere", + "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere", "body": {} }, "mockResponse": { diff --git a/config/config.go b/config/config.go index 3b34d3a4815..79a31db154a 100755 --- a/config/config.go +++ b/config/config.go @@ -718,7 +718,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") - v.SetDefault("adapters.eplanning.endpoint", "https://ads.us.e-planning.net/hb/1") + v.SetDefault("adapters.eplanning.endpoint", "https://ads.us.e-planning.net/pbs/1") v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/") v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io") v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") From cd9116e80c514a846600a4c14f57e608ffaedf32 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 3 Jun 2020 14:33:07 -0400 Subject: [PATCH 103/318] Enable full TCF2 support (#1302) * New config options * Enble TCF2 fields and logic * Resolves some PR comments * More tests * gofmt * Added enforcement tests for split GDPR/GDPRGeo * Testing tweaks * No longer ignore enforce purpose 1 on allowSync() * Removes Purpose 4 --- config/config.go | 29 +++ endpoints/auction_test.go | 5 +- endpoints/cookie_sync_test.go | 4 +- endpoints/setuid_test.go | 4 +- exchange/utils.go | 4 +- exchange/utils_test.go | 6 +- gdpr/gdpr.go | 2 +- gdpr/impl.go | 96 ++++++-- gdpr/impl_test.go | 390 ++++++++++++++++++++++++++++++- gdpr/vendorlist-fetching_test.go | 86 ++++++- go.mod | 3 +- go.sum | 6 +- privacy/enforcement.go | 19 +- privacy/enforcement_test.go | 108 ++++++--- 14 files changed, 677 insertions(+), 85 deletions(-) diff --git a/config/config.go b/config/config.go index 79a31db154a..5f19629d2db 100755 --- a/config/config.go +++ b/config/config.go @@ -142,6 +142,7 @@ type GDPR struct { Timeouts GDPRTimeouts `mapstructure:"timeouts_ms"` NonStandardPublishers []string `mapstructure:"non_standard_publishers,flow"` NonStandardPublisherMap map[string]int + TCF2 TCF2 `mapstructure:"tcf2"` AMPException bool `mapstructure:"amp_exception"` } @@ -165,6 +166,26 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration { return time.Duration(t.ActiveVendorlistFetch) * time.Millisecond } +// TCF2 defines the TCF2 specific configurations for GDPR +type TCF2 struct { + Enabled bool `mapstructure:"enabled"` + Purpose1 PurposeDetail `mapstructure:"purpose1"` + Purpose2 PurposeDetail `mapstructure:"purpose2"` + Purpose7 PurposeDetail `mapstructure:"purpose7"` + SpecialPurpose1 PurposeDetail `mapstructure:"special_purpose1"` + PurposeOneTreatment PurposeOneTreatement `mapstructure:"purpose_one_treatement"` +} + +// Making a purpose struct so purpose specific details can be added later. +type PurposeDetail struct { + Enabled bool `mapstructure:"enabled"` +} + +type PurposeOneTreatement struct { + Enabled bool `mapstructure:"enabled"` + AccessAllowed bool `mapstructure:"access_allowed"` +} + type CCPA struct { Enforce bool `mapstructure:"enforce"` } @@ -774,6 +795,14 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0) v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0) v.SetDefault("gdpr.non_standard_publishers", []string{""}) + v.SetDefault("gdpr.tcf2.enabled", true) + v.SetDefault("gdpr.tcf2.purpose1.enabled", true) + v.SetDefault("gdpr.tcf2.purpose2.enabled", true) + v.SetDefault("gdpr.tcf2.purpose4.enabled", true) + v.SetDefault("gdpr.tcf2.purpose7.enabled", true) + v.SetDefault("gdpr.tcf2.special_purpose1.enabled", true) + v.SetDefault("gdpr.tcf2.purpose_one_treatement.enabled", true) + v.SetDefault("gdpr.tcf2.purpose_one_treatement.access_allowed", true) v.SetDefault("gdpr.amp_exception", false) v.SetDefault("ccpa.enforce", false) v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go index 3035a6d45fb..5e9e9639a9c 100644 --- a/endpoints/auction_test.go +++ b/endpoints/auction_test.go @@ -407,6 +407,7 @@ type auctionMockPermissions struct { allowBidderSync bool allowHostCookies bool allowPI bool + allowGeo bool } func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { @@ -417,8 +418,8 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o return m.allowBidderSync, nil } -func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { - return m.allowPI, nil +func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { + return m.allowPI, m.allowGeo, nil } func (m *auctionMockPermissions) AMPException() bool { diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index bb766aa92e7..824e32f1957 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -254,8 +254,8 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi return ok, nil } -func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { - return true, nil +func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { + return true, true, nil } func (g *gdprPerms) AMPException() bool { diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 8499ac1ca5d..3f47b257d2e 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -437,8 +437,8 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return false, nil } -func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { - return g.allowPI, nil +func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { + return g.allowPI, g.allowPI, nil } func (g *mockPermsSetUID) AMPException() bool { diff --git a/exchange/utils.go b/exchange/utils.go index d961089c4cb..f602d1e8fba 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -60,10 +60,12 @@ func cleanOpenRTBRequests(ctx context.Context, coreBidder := resolveBidder(bidder.String(), aliases) var publisherID = labels.PubID - ok, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) + ok, geo, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) privacyEnforcement.GDPR = !ok && err == nil + privacyEnforcement.GDPRGeo = !geo && err == nil } else { privacyEnforcement.GDPR = false + privacyEnforcement.GDPRGeo = false } privacyEnforcement.Apply(bidReq, ampGDPRException) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 53d6b85c243..acbf25ff691 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -24,11 +24,11 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return true, nil } -func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { +func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { if bidder == "appnexus" { - return true, nil + return true, true, nil } - return false, nil + return false, false, nil } func (p *permissionsMock) AMPException() bool { diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index 9390d942f80..0dfa12f5ebd 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -23,7 +23,7 @@ type Permissions interface { // Determines whether or not to send PI information to a bidder, or mask it out. // // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. - PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) + PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) // Exposes the AMP execption flag AMPException() bool diff --git a/gdpr/impl.go b/gdpr/impl.go index 8743d7f2778..60db804aec6 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -2,11 +2,13 @@ package gdpr import ( "context" + "fmt" "github.com/prebid/go-gdpr/api" tcf1constants "github.com/prebid/go-gdpr/consentconstants" consentconstants "github.com/prebid/go-gdpr/consentconstants/tcf2" "github.com/prebid/go-gdpr/vendorconsent" + tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" @@ -40,10 +42,10 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return false, nil } -func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { +func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { _, ok := p.cfg.NonStandardPublisherMap[PublisherID] if ok { - return true, nil + return true, true, nil } id, ok := p.vendorIDs[bidder] @@ -52,10 +54,10 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt } if consent == "" { - return p.cfg.UsersyncIfAmbiguous, nil + return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil } - return false, nil + return false, false, nil } func (p *permissionsImpl) AMPException() bool { @@ -78,38 +80,104 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen } // InfoStorageAccess is the same across TCF 1 and TCF 2 + if parsedConsent.Version() == 2 { + if !p.cfg.TCF2.Purpose1.Enabled { + // We are not enforcing purpose 1 + return true, nil + } + consent, ok := parsedConsent.(tcf2.ConsentMetadata) + if !ok { + err := fmt.Errorf("Unable to access TCF2 parsed consent") + return false, err + } + return p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess), nil + } if vendor.Purpose(consentconstants.InfoStorageAccess) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && parsedConsent.VendorConsent(vendorID) { return true, nil } return false, nil } -func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, error) { +func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, bool, error) { // If we're not given a consent string, respect the preferences in the app config. if consent == "" { - return p.cfg.UsersyncIfAmbiguous, nil + return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil } parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent) if err != nil { - return false, err + return false, false, err } if vendor == nil { - return false, nil + return false, false, nil } if parsedConsent.Version() == 2 { - // Need to add the location special purpose once the library supports it. + if p.cfg.TCF2.Enabled { + return p.allowPITCF2(parsedConsent, vendor, vendorID) + } if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile)) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && parsedConsent.VendorConsent(vendorID) { - return true, nil + return true, true, nil } } else { if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) { - return true, nil + return true, true, nil } } - return false, nil + return false, false, nil +} + +func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16) (allowPI bool, allowGeo bool, err error) { + consent, ok := parsedConsent.(tcf2.ConsentMetadata) + err = nil + allowPI = false + allowGeo = false + if !ok { + err = fmt.Errorf("Unable to access TCF2 parsed consent") + return + } + if p.cfg.TCF2.SpecialPurpose1.Enabled { + allowGeo = consent.SpecialFeatureOptIn(1) && vendor.SpecialPurpose(1) + } else { + allowGeo = true + } + // Set to true so any purpose check can flip it to false + allowPI = true + if p.cfg.TCF2.Purpose1.Enabled { + allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.InfoStorageAccess) + } + if p.cfg.TCF2.Purpose2.Enabled { + allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.BasicAdserving) + } + if p.cfg.TCF2.Purpose7.Enabled { + allowPI = allowPI && p.checkPurpose(consent, vendor, vendorID, consentconstants.AdPerformance) + } + return +} + +const pubRestrictNotAllowed = 0 +const pubRestrictRequireConsent = 1 +const pubRestrictRequireLegitInterest = 2 + +func (p *permissionsImpl) checkPurpose(consent tcf2.ConsentMetadata, vendor api.Vendor, vendorID uint16, purpose tcf1constants.Purpose) bool { + if purpose == consentconstants.InfoStorageAccess && p.cfg.TCF2.PurposeOneTreatment.Enabled && consent.PurposeOneTreatment() { + return p.cfg.TCF2.PurposeOneTreatment.AccessAllowed + } + if consent.CheckPubRestriction(uint8(purpose), pubRestrictNotAllowed, vendorID) { + return false + } + if consent.CheckPubRestriction(uint8(purpose), pubRestrictRequireConsent, vendorID) { + return vendor.PurposeStrict(purpose) && consent.PurposeAllowed(purpose) && consent.VendorConsent(vendorID) + } + if consent.CheckPubRestriction(uint8(purpose), pubRestrictRequireLegitInterest, vendorID) { + // Need LITransparency here + return vendor.LegitimateInterestStrict(purpose) && consent.PurposeLITransparency(purpose) && consent.VendorLegitInterest(vendorID) + } + purposeAllowed := vendor.Purpose(purpose) && consent.PurposeAllowed(purpose) && consent.VendorConsent(vendorID) + legitInterest := vendor.LegitimateInterest(purpose) && consent.PurposeLITransparency(purpose) && consent.VendorLegitInterest(vendorID) + + return purposeAllowed || legitInterest } func (p *permissionsImpl) parseVendor(ctx context.Context, vendorID uint16, consent string) (parsedConsent api.VendorConsents, vendor api.Vendor, err error) { @@ -146,8 +214,8 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B return true, nil } -func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, error) { - return true, nil +func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { + return true, true, nil } func (a AlwaysAllow) AMPException() bool { diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index 8b89577d6c8..f05f25e87ea 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -10,6 +10,9 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/go-gdpr/vendorlist" + "github.com/prebid/go-gdpr/vendorlist2" + + "github.com/stretchr/testify/assert" ) func TestNoConsentButAllowByDefault(t *testing.T) { @@ -55,10 +58,10 @@ func TestNoConsentAndRejectByDefault(t *testing.T) { func TestAllowedSyncs(t *testing.T) { vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ 2: { - purposes: []uint8{1}, + purposes: []int{1}, }, 3: { - purposes: []uint8{1}, + purposes: []int{1}, }, }) perms := permissionsImpl{ @@ -91,10 +94,10 @@ func TestAllowedSyncs(t *testing.T) { func TestProhibitedPurposes(t *testing.T) { vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ 2: { - purposes: []uint8{1}, // cookie reads/writes + purposes: []int{1}, // cookie reads/writes }, 3: { - purposes: []uint8{3}, // ad personalization + purposes: []int{3}, // ad personalization }, }) perms := permissionsImpl{ @@ -127,10 +130,10 @@ func TestProhibitedPurposes(t *testing.T) { func TestProhibitedVendors(t *testing.T) { vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ 2: { - purposes: []uint8{1}, // cookie reads/writes + purposes: []int{1}, // cookie reads/writes }, 3: { - purposes: []uint8{3}, // ad personalization + purposes: []int{3}, // ad personalization }, }) perms := permissionsImpl{ @@ -179,10 +182,10 @@ func TestMalformedConsent(t *testing.T) { func TestAllowPersonalInfo(t *testing.T) { vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ 2: { - purposes: []uint8{1}, // cookie reads/writes + purposes: []int{1}, // cookie reads/writes }, 3: { - purposes: []uint8{1, 3}, // ad personalization + purposes: []int{1, 3}, // ad personalization }, }) perms := permissionsImpl{ @@ -204,21 +207,377 @@ func TestAllowPersonalInfo(t *testing.T) { } // PI needs both purposes to succeed - allowPI, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, false, allowPI) - allowPI, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, true, allowPI) // Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1} - allowPI, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, true, allowPI) } +var tcf2BasicPurposes = map[uint16]*purposes{ + 2: {purposes: []int{1}}, //cookie reads/writes + 6: {purposes: []int{1, 2, 4}}, // ad personalization + 8: {purposes: []int{1, 7}}, + 10: {purposes: []int{2, 4, 7}}, + 32: {purposes: []int{1, 2, 4, 7}}, +} +var tcf2LegitInterests = map[uint16]*purposes{ + 6: {purposes: []int{7}}, + 8: {purposes: []int{2, 4}}, +} +var tcf2SpecialPuproses = map[uint16]*purposes{ + 6: {purposes: []int{1}}, + 10: {purposes: []int{1}}, +} +var tcf2FlexPurposes = map[uint16]*purposes{ + 6: {purposes: []int{1, 2, 4, 7}}, +} +var tcf2Config = config.GDPR{ + HostVendorID: 2, + TCF2: config.TCF2{ + Enabled: true, + Purpose1: config.PurposeDetail{Enabled: true}, + Purpose2: config.PurposeDetail{Enabled: true}, + Purpose7: config.PurposeDetail{Enabled: true}, + SpecialPurpose1: config.PurposeDetail{Enabled: true}, + }, +} + +type tcf2TestDef struct { + description string + bidder openrtb_ext.BidderName + consent string + allowPI bool + allowGeo bool +} + +func TestAllowPersonalInfoTCF2(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 6, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 + // PI needs all purposes to succeed + testDefs := []tcf2TestDef{ + { + description: "Appnexus vendor test, insufficient purposes claimed", + bidder: openrtb_ext.BidderAppnexus, + consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + allowPI: false, + allowGeo: false, + }, + { + description: "Pubmatic vendor test, flex purposes claimed", + bidder: openrtb_ext.BidderPubmatic, + consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + allowPI: true, + allowGeo: true, + }, + { + description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", + bidder: openrtb_ext.BidderRubicon, + consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + allowPI: true, + allowGeo: false, + }, + } + + for _, td := range testDefs { + allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) + assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) + assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + } +} + +func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 6, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + // Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array + perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1} + allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed") + assert.EqualValuesf(t, true, allowPI, "AllowPI failure") + assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure") + +} + +func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 32, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 15: parseVendorListDataV2(t, vendorListData), + }), + }, + } + + // COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA - vendors 1-10 legit interest only, + // Pub restriction on purpose 7, consent only ... no allowPI will pass, no Special purpose 1 consent + testDefs := []tcf2TestDef{ + { + description: "Appnexus vendor test, insufficient purposes claimed", + bidder: openrtb_ext.BidderAppnexus, + consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", + allowPI: false, + allowGeo: false, + }, + { + description: "Pubmatic vendor test, flex purposes claimed", + bidder: openrtb_ext.BidderPubmatic, + consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", + allowPI: false, + allowGeo: false, + }, + { + description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", + bidder: openrtb_ext.BidderRubicon, + consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", + allowPI: false, + allowGeo: false, + }, + } + + for _, td := range testDefs { + allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) + assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) + assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + } +} + +func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 10, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + perms.cfg.TCF2.PurposeOneTreatment.Enabled = true + perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = true + + // COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set + testDefs := []tcf2TestDef{ + { + description: "Appnexus vendor test, insufficient purposes claimed", + bidder: openrtb_ext.BidderAppnexus, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: false, + allowGeo: false, + }, + { + description: "Pubmatic vendor test, flex purposes claimed", + bidder: openrtb_ext.BidderPubmatic, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: true, + allowGeo: true, + }, + { + description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", + bidder: openrtb_ext.BidderRubicon, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: true, + allowGeo: false, + }, + } + + for _, td := range testDefs { + allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) + assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) + assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + } +} + +func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 10, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + perms.cfg.TCF2.PurposeOneTreatment.Enabled = true + perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = false + + // COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set + testDefs := []tcf2TestDef{ + { + description: "Appnexus vendor test, insufficient purposes claimed", + bidder: openrtb_ext.BidderAppnexus, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: false, + allowGeo: false, + }, + { + description: "Pubmatic vendor test, flex purposes claimed", + bidder: openrtb_ext.BidderPubmatic, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: false, + allowGeo: true, + }, + { + description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", + bidder: openrtb_ext.BidderRubicon, + consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", + allowPI: false, + allowGeo: false, + }, + } + + for _, td := range testDefs { + allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) + assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) + assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + } +} + +func TestAllowSyncTCF2(t *testing.T) { + vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 6, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 + allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") + assert.EqualValuesf(t, true, allowSync, "HostCookiesAllowed failure") + + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") + assert.EqualValuesf(t, true, allowSync, "BidderSyncAllowed failure") +} + +func TestProhibitedPurposeSyncTCF2(t *testing.T) { + basicPurposes := tcf2BasicPurposes + basicPurposes[8] = &purposes{purposes: []int{7}} + vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 6, + openrtb_ext.BidderRubicon: 8, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + perms.cfg.HostVendorID = 8 + + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 + allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") + assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") + + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") + assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure") +} + +func TestProhibitedVendorSyncTCF2(t *testing.T) { + basicPurposes := tcf2BasicPurposes + basicPurposes[10] = &purposes{purposes: []int{1}} + vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + perms := permissionsImpl{ + cfg: tcf2Config, + vendorIDs: map[openrtb_ext.BidderName]uint16{ + openrtb_ext.BidderAppnexus: 2, + openrtb_ext.BidderPubmatic: 6, + openrtb_ext.BidderRubicon: 8, + openrtb_ext.BidderOpenx: 10, + }, + fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ + tCF1: nil, + tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + 34: parseVendorListDataV2(t, vendorListData), + }), + }, + } + perms.cfg.HostVendorID = 10 + + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 4, 6 + allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") + assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") + + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") + assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure") +} + func parseVendorListData(t *testing.T, data string) vendorlist.VendorList { t.Helper() parsed, err := vendorlist.ParseEagerly([]byte(data)) @@ -228,6 +587,15 @@ func parseVendorListData(t *testing.T, data string) vendorlist.VendorList { return parsed } +func parseVendorListDataV2(t *testing.T, data string) vendorlist.VendorList { + t.Helper() + parsed, err := vendorlist2.ParseEagerly([]byte(data)) + if err != nil { + t.Fatalf("Failed to parse vendor list data. %v", err) + } + return parsed +} + func listFetcher(lists map[uint16]vendorlist.VendorList) func(context.Context, uint16) (vendorlist.VendorList, error) { return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { data, ok := lists[id] diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 8197fa263bc..824f9178faa 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -15,12 +15,12 @@ import ( func TestVendorFetch(t *testing.T) { vendorListOne := mockVendorListData(t, 1, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2}, + purposes: []int{1, 2}, }, }) vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2, 3}, + purposes: []int{1, 2, 3}, }, }) server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ @@ -47,12 +47,12 @@ func TestVendorFetch(t *testing.T) { func TestLazyFetch(t *testing.T) { firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2}, + purposes: []int{1, 2}, }, }) secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ 3: { - purposes: []uint8{1}, + purposes: []int{1}, }, }) server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ @@ -73,7 +73,7 @@ func TestLazyFetch(t *testing.T) { func TestInitialTimeout(t *testing.T) { list := mockVendorListData(t, 1, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2}, + purposes: []int{1, 2}, }, }) server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ @@ -91,12 +91,12 @@ func TestInitialTimeout(t *testing.T) { func TestFetchThrottling(t *testing.T) { vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2}, + purposes: []int{1, 2}, }, }) vendorListThree := mockVendorListData(t, 3, map[uint16]*purposes{ 32: { - purposes: []uint8{1, 2}, + purposes: []int{1, 2}, }, }) server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ @@ -174,8 +174,8 @@ func mockServer(latestVersion int, responses map[int]string) func(http.ResponseW func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purposes) string { type vendorContract struct { - ID uint16 `json:"id"` - Purposes []uint8 `json:"purposeIds"` + ID uint16 `json:"id"` + Purposes []int `json:"purposeIds"` } type vendorListContract struct { @@ -203,6 +203,72 @@ func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purpos return string(data) } +type purposeMap map[uint16]*purposes + +func mockVendorListDataTCF2(t *testing.T, version uint16, basicPurposes purposeMap, legitInterests purposeMap, flexPurposes purposeMap, specialPurposes purposeMap) string { + type vendorContract struct { + ID uint16 `json:"id"` + Purposes []int `json:"purposes"` + LegIntPurposes []int `json:"legIntPurposes"` + FlexiblePurposes []int `json:"flexiblePurposes"` + SpecialPurposes []int `json:"specialPurposes"` + } + + type vendorListContract struct { + Version uint16 `json:"vendorListVersion"` + Vendors map[string]vendorContract `json:"vendors"` + } + + vendors := make(map[string]vendorContract, len(basicPurposes)) + for id, purpose := range basicPurposes { + sid := strconv.Itoa(int(id)) + vendor, ok := vendors[sid] + if !ok { + vendor = vendorContract{ID: id} + } + vendor.Purposes = purpose.purposes + vendors[sid] = vendor + } + + for id, purpose := range legitInterests { + sid := strconv.Itoa(int(id)) + vendor, ok := vendors[sid] + if !ok { + vendor = vendorContract{ID: id} + } + vendor.LegIntPurposes = purpose.purposes + vendors[sid] = vendor + } + + for id, purpose := range flexPurposes { + sid := strconv.Itoa(int(id)) + vendor, ok := vendors[sid] + if !ok { + vendor = vendorContract{ID: id} + } + vendor.FlexiblePurposes = purpose.purposes + vendors[sid] = vendor + } + + for id, purpose := range specialPurposes { + sid := strconv.Itoa(int(id)) + vendor, ok := vendors[sid] + if !ok { + vendor = vendorContract{ID: id} + } + vendor.SpecialPurposes = purpose.purposes + vendors[sid] = vendor + } + + obj := vendorListContract{ + Version: version, + Vendors: vendors, + } + data, err := json.Marshal(obj) + assertNilErr(t, err) + return string(data) +} + func testURLMaker(server *httptest.Server) func(uint16, uint8) string { url := server.URL return func(version uint16, TCFVer uint8) string { @@ -220,5 +286,5 @@ func testConfig() config.GDPR { } type purposes struct { - purposes []uint8 + purposes []int } diff --git a/go.mod b/go.mod index 8de6f10e4b9..0224057e464 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,8 @@ require ( github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect - github.com/prebid/go-gdpr v0.7.0 + github.com/prebid/go-gdpr v0.8.2 + github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect diff --git a/go.sum b/go.sum index 176bacfc20a..5d941b89e90 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,10 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prebid/go-gdpr v0.7.0 h1:m4E/FjUhTBMciDsd3lQlbzFyXLzNK+JQkFmInJpFAwc= -github.com/prebid/go-gdpr v0.7.0/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= +github.com/prebid/go-gdpr v0.8.2 h1:mN2jKYZZpJkCYFQB/nDTJoPpuGYblOYP2UUzOzRggII= +github.com/prebid/go-gdpr v0.8.2/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= +github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= +github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= diff --git a/privacy/enforcement.go b/privacy/enforcement.go index 96d03ef4433..d302192ec3f 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -6,14 +6,15 @@ import ( // Enforcement represents the privacy policies to enforce for an OpenRTB bid request. type Enforcement struct { - CCPA bool - COPPA bool - GDPR bool + CCPA bool + COPPA bool + GDPR bool + GDPRGeo bool } // Any returns true if at least one privacy policy requires enforcement. func (e Enforcement) Any() bool { - return e.CCPA || e.COPPA || e.GDPR + return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo } // Apply cleans personally identifiable information from an OpenRTB bid request. @@ -45,7 +46,7 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { return ScrubStrategyGeoFull } - if e.GDPR || e.CCPA { + if e.GDPRGeo || e.CCPA { return ScrubStrategyGeoReducedPrecision } @@ -60,5 +61,11 @@ func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUs if e.GDPR && ampGDPRException { return ScrubStrategyUserNone } - return ScrubStrategyUserID + + // If no user scrubbing is needed, then return none, else scrub ID (COPPA checked above) + if e.CCPA || e.GDPR { + return ScrubStrategyUserID + } + + return ScrubStrategyUserNone } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index 25e08b5e80d..0e82648d4b9 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -17,27 +17,40 @@ func TestAny(t *testing.T) { { description: "All False", enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPR: false, + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: false, }, expected: false, }, { description: "All True", enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPR: true, + CCPA: true, + COPPA: true, + GDPR: true, + GDPRGeo: true, }, expected: true, }, { description: "Mixed", enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPR: false, + CCPA: false, + COPPA: true, + GDPR: false, + GDPRGeo: false, + }, + expected: true, + }, + { + description: "GDPRGeo only", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: true, }, expected: true, }, @@ -62,9 +75,10 @@ func TestApply(t *testing.T) { { description: "All Enforced", enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPR: true, + CCPA: true, + COPPA: true, + GDPR: true, + GDPRGeo: true, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -75,9 +89,10 @@ func TestApply(t *testing.T) { { description: "CCPA Only", enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: false, + CCPA: true, + COPPA: false, + GDPR: false, + GDPRGeo: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -88,9 +103,10 @@ func TestApply(t *testing.T) { { description: "COPPA Only", enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPR: false, + CCPA: false, + COPPA: true, + GDPR: false, + GDPRGeo: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -101,9 +117,10 @@ func TestApply(t *testing.T) { { description: "GDPR Only", enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPR: true, + CCPA: false, + COPPA: false, + GDPR: true, + GDPRGeo: true, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -114,9 +131,10 @@ func TestApply(t *testing.T) { { description: "GDPR Only, ampGDPRException", enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPR: true, + CCPA: false, + COPPA: false, + GDPR: true, + GDPRGeo: true, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -127,9 +145,10 @@ func TestApply(t *testing.T) { { description: "CCPA Only, ampGDPRException", enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: false, + CCPA: true, + COPPA: false, + GDPR: false, + GDPRGeo: false, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -140,9 +159,10 @@ func TestApply(t *testing.T) { { description: "COPPA and GDPR, ampGDPRException", enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPR: true, + CCPA: false, + COPPA: true, + GDPR: true, + GDPRGeo: true, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -150,6 +170,34 @@ func TestApply(t *testing.T) { expectedUser: ScrubStrategyUserIDAndDemographic, expectedUserGeo: ScrubStrategyGeoFull, }, + { + description: "GDPR Only, no Geo", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: true, + GDPRGeo: false, + }, + ampGDPRException: false, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceGeo: ScrubStrategyGeoNone, + expectedUser: ScrubStrategyUserID, + expectedUserGeo: ScrubStrategyGeoNone, + }, + { + description: "GDPR Only, Geo only", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: true, + }, + ampGDPRException: false, + expectedDeviceIPv6: ScrubStrategyIPV6None, + expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedUser: ScrubStrategyUserNone, + expectedUserGeo: ScrubStrategyGeoReducedPrecision, + }, } for _, test := range testCases { From 23c684c5a6c4189ca172a83453b2cfa8de2bf0a5 Mon Sep 17 00:00:00 2001 From: Seba Perez Date: Wed, 3 Jun 2020 15:40:46 -0300 Subject: [PATCH 104/318] Change on eplanning endpoint (hostname) (#1328) --- adapters/eplanning/eplanning_test.go | 2 +- adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json | 2 +- adapters/eplanning/eplanningtest/exemplary/simple-banner.json | 2 +- adapters/eplanning/eplanningtest/exemplary/two-banners.json | 2 +- .../supplemental/app-domain-and-url-correctly-parsed.json | 2 +- .../eplanningtest/supplemental/banner-no-size-sends-1x1.json | 2 +- .../eplanningtest/supplemental/invalid-response-no-bids.json | 2 +- .../supplemental/invalid-response-unmarshall-error.json | 2 +- .../eplanningtest/supplemental/server-bad-request.json | 2 +- .../eplanning/eplanningtest/supplemental/server-error-code.json | 2 +- .../eplanning/eplanningtest/supplemental/server-no-content.json | 2 +- .../supplemental/site-domain-and-url-correctly-parsed.json | 2 +- config/config.go | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/adapters/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go index 461fb849ced..28fdf6c45c2 100644 --- a/adapters/eplanning/eplanning_test.go +++ b/adapters/eplanning/eplanning_test.go @@ -8,7 +8,7 @@ import ( ) func TestJsonSamples(t *testing.T) { - eplanningAdapter := NewEPlanningBidder(new(http.Client), "https://ads.us.e-planning.net/pbs/1") + eplanningAdapter := NewEPlanningBidder(new(http.Client), "http://rtb.e-planning.net/pbs/1") eplanningAdapter.testing = true adapterstest.RunJSONBidderTest(t, "eplanningtest", eplanningAdapter) } diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json index a67a86d18e6..556831217ec 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner-2.json @@ -28,7 +28,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=300x250%3A300x250&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json index 9146bb4afb5..04eca985340 100644 --- a/adapters/eplanning/eplanningtest/exemplary/simple-banner.json +++ b/adapters/eplanning/eplanningtest/exemplary/simple-banner.json @@ -31,7 +31,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&uid=2154987&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadun_itco_de%3A600x300&ip=123.123.123.123&ncb=1&uid=2154987&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/exemplary/two-banners.json b/adapters/eplanning/eplanningtest/exemplary/two-banners.json index 174c8ce3fc6..72aeb64b3a9 100644 --- a/adapters/eplanning/eplanningtest/exemplary/two-banners.json +++ b/adapters/eplanning/eplanningtest/exemplary/two-banners.json @@ -39,7 +39,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300%2B300x250%3A300x250&ip=123.123.123.123&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json index 04df82f6668..413c973dfa2 100644 --- a/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json +++ b/adapters/eplanning/eplanningtest/supplemental/app-domain-and-url-correctly-parsed.json @@ -39,7 +39,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/mx.com.xeu/ROS?app=1&appid=%5Ba-f0-9%5D%7B16%7D&appn=MobileExchange&e=testadunitcode%3A600x300&ifa=3B8E2335-Z049&ip=123.123.123.123&ncb=1", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json index f02eb80fe41..05547d81707 100644 --- a/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json +++ b/adapters/eplanning/eplanningtest/supplemental/banner-no-size-sends-1x1.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcodenosize%3A1x1&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json index 8bdcfddd733..570488825e2 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-no-bids.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json index 9f5b2d7fc03..4ba2d44bf3a 100644 --- a/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json +++ b/adapters/eplanning/eplanningtest/supplemental/invalid-response-unmarshall-error.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json index 2ef03648884..9e8eae8c080 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-bad-request.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json index 76e75a5c203..08f46d9e6c2 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-error-code.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-error-code.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json index 02f1fa46d33..20a2b1cf456 100644 --- a/adapters/eplanning/eplanningtest/supplemental/server-no-content.json +++ b/adapters/eplanning/eplanningtest/supplemental/server-no-content.json @@ -21,7 +21,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/FILE/ROS?e=testadunitcode%3A600x300&ncb=1&ur=FILE", "body": {} }, "mockResponse": { diff --git a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json index 581cb1d5b46..62890d914ff 100644 --- a/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json +++ b/adapters/eplanning/eplanningtest/supplemental/site-domain-and-url-correctly-parsed.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ads.us.e-planning.net/pbs/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere", + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere", "body": {} }, "mockResponse": { diff --git a/config/config.go b/config/config.go index 5f19629d2db..9652ae141f5 100755 --- a/config/config.go +++ b/config/config.go @@ -739,7 +739,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") - v.SetDefault("adapters.eplanning.endpoint", "https://ads.us.e-planning.net/pbs/1") + v.SetDefault("adapters.eplanning.endpoint", "http://rtb.e-planning.net/pbs/1") v.SetDefault("adapters.gamma.endpoint", "https://hb.gammaplatform.com/adx/request/") v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io") v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") From 352784573cbdb29d725c45328dda7fa335096116 Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Wed, 3 Jun 2020 14:43:31 -0400 Subject: [PATCH 105/318] Districtm Dmx: new adapter (#1209) Co-authored-by: steve-a-districtm --- adapters/dmx/dmx.go | 296 +++++++ adapters/dmx/dmx_test.go | 782 ++++++++++++++++++ .../dmx/dmxtest/exemplary/simple-app.json | 138 ++++ .../dmx/dmxtest/exemplary/simple-banner.json | 126 +++ .../dmx/dmxtest/exemplary/simple-video.json | 112 +++ adapters/dmx/dmxtest/params/race/banner.json | 4 + adapters/dmx/dmxtest/params/race/video.json | 4 + adapters/dmx/usersync.go | 12 + adapters/dmx/usersync_test.go | 20 + config/config.go | 4 +- exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + static/bidder-info/dmx.yaml | 11 + static/bidder-params/dmx.json | 22 + usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 16 files changed, 1537 insertions(+), 1 deletion(-) create mode 100644 adapters/dmx/dmx.go create mode 100644 adapters/dmx/dmx_test.go create mode 100644 adapters/dmx/dmxtest/exemplary/simple-app.json create mode 100644 adapters/dmx/dmxtest/exemplary/simple-banner.json create mode 100644 adapters/dmx/dmxtest/exemplary/simple-video.json create mode 100644 adapters/dmx/dmxtest/params/race/banner.json create mode 100644 adapters/dmx/dmxtest/params/race/video.json create mode 100644 adapters/dmx/usersync.go create mode 100644 adapters/dmx/usersync_test.go create mode 100644 static/bidder-info/dmx.yaml create mode 100644 static/bidder-params/dmx.json diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go new file mode 100644 index 00000000000..6b4f698d4b1 --- /dev/null +++ b/adapters/dmx/dmx.go @@ -0,0 +1,296 @@ +package dmx + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "net/url" + "strings" +) + +type DmxAdapter struct { + endpoint string +} + +func NewDmxBidder(endpoint string) *DmxAdapter { + return &DmxAdapter{endpoint: endpoint} +} + +type dmxExt struct { + Bidder dmxParams `json:"bidder"` +} + +type dmxParams struct { + TagId string `json:"tagid,omitempty"` + DmxId string `json:"dmxid,omitempty"` + MemberId string `json:"memberid,omitempty"` + PublisherId string `json:"publisher_id,omitempty"` + SellerId string `json:"seller_id,omitempty"` +} + +func UserSellerOrPubId(str1, str2 string) string { + if str1 != "" { + return str1 + } + return str2 +} + +func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapters.ExtraRequestInfo) (reqsBidder []*adapters.RequestData, errs []error) { + var imps []openrtb.Imp + var rootExtInfo dmxExt + var publisherId string + var sellerId string + var userExt openrtb_ext.ExtUser + var anyHasId = false + var reqCopy openrtb.BidRequest = *request + var dmxReq *openrtb.BidRequest = &reqCopy + + if request.User == nil { + if request.App == nil { + return nil, []error{errors.New("No user id or app id found. Could not send request to DMX.")} + } + } + + if len(request.Imp) >= 1 { + err := json.Unmarshal(request.Imp[0].Ext, &rootExtInfo) + if err != nil { + errs = append(errs, err) + } else { + publisherId = UserSellerOrPubId(rootExtInfo.Bidder.PublisherId, rootExtInfo.Bidder.MemberId) + sellerId = rootExtInfo.Bidder.SellerId + } + } + + if request.App != nil { + appCopy := *request.App + appPublisherCopy := *request.App.Publisher + dmxReq.App = &appCopy + dmxReq.App.Publisher = &appPublisherCopy + if dmxReq.App.ID != "" { + anyHasId = true + } + } else { + dmxReq.App = nil + } + + if request.Site != nil { + siteCopy := *request.Site + sitePublisherCopy := *request.Site.Publisher + dmxReq.Site = &siteCopy + dmxReq.Site.Publisher = &sitePublisherCopy + if dmxReq.Site.Publisher != nil { + dmxReq.Site.Publisher.ID = publisherId + } else { + dmxReq.Site.Publisher = &openrtb.Publisher{ID: publisherId} + } + } else { + dmxReq.Site = nil + } + + if request.User != nil { + userCopy := *request.User + dmxReq.User = &userCopy + } else { + dmxReq.User = nil + } + + if dmxReq.User != nil { + if dmxReq.User.ID != "" { + anyHasId = true + } + if dmxReq.User.Ext != nil { + if err := json.Unmarshal(dmxReq.User.Ext, &userExt); err == nil { + if len(userExt.Eids) > 0 || (userExt.DigiTrust != nil && userExt.DigiTrust.ID != "") { + anyHasId = true + } + } + } + } + + if anyHasId == false { + return nil, []error{errors.New("This request contained no identifier")} + } + + for _, inst := range dmxReq.Imp { + var banner *openrtb.Banner + var video *openrtb.Video + var ins openrtb.Imp + var params dmxExt + const intVal int8 = 1 + source := (*json.RawMessage)(&inst.Ext) + if err := json.Unmarshal(*source, ¶ms); err != nil { + errs = append(errs, err) + } + if isDmxParams(params.Bidder) { + if inst.Banner != nil { + if len(inst.Banner.Format) != 0 { + banner = inst.Banner + if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" { + imps = fetchParams(params, inst, ins, imps, banner, nil, intVal) + } else { + return nil, []error{errors.New("Missing Params for auction to be send")} + } + } + } + + if inst.Video != nil { + video = inst.Video + if params.Bidder.PublisherId != "" || params.Bidder.MemberId != "" { + imps = fetchParams(params, inst, ins, imps, nil, video, intVal) + } else { + return nil, []error{errors.New("Missing Params for auction to be send")} + } + } + } + + } + + dmxReq.Imp = imps + + oJson, err := json.Marshal(dmxReq) + + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "Application/json;charset=utf-8") + reqBidder := &adapters.RequestData{ + Method: "POST", + Uri: adapter.endpoint + addParams(sellerId), //adapter.endpoint, + Body: oJson, + Headers: headers, + } + reqsBidder = append(reqsBidder, reqBidder) + return +} + +func (adapter *DmxAdapter) MakeBids(request *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + if http.StatusNoContent == response.StatusCode { + return nil, nil + } + + if http.StatusBadRequest == response.StatusCode { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code 400"), + }} + } + + if http.StatusOK != response.StatusCode { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected response no status code"), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, request.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + if b.BidType == openrtb_ext.BidTypeVideo { + b.Bid.AdM = videoImpInsertion(b.Bid) + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs + +} + +func fetchParams(params dmxExt, inst openrtb.Imp, ins openrtb.Imp, imps []openrtb.Imp, banner *openrtb.Banner, video *openrtb.Video, intVal int8) []openrtb.Imp { + if params.Bidder.TagId != "" { + ins = openrtb.Imp{ + ID: inst.ID, + TagID: params.Bidder.TagId, + Ext: inst.Ext, + Secure: &intVal, + } + } + + if params.Bidder.DmxId != "" { + ins = openrtb.Imp{ + ID: inst.ID, + TagID: params.Bidder.DmxId, + Ext: inst.Ext, + Secure: &intVal, + } + } + if banner != nil { + ins.Banner = banner + } + + if video != nil { + ins.Video = video + } + + if ins.TagID == "" { + return imps + } + imps = append(imps, ins) + return imps +} + +func addParams(str string) string { + if str != "" { + return "?sellerid=" + url.QueryEscape(str) + } + return "" +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner == nil && imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } + return mediaType, nil + } + } + + // This shouldnt happen. Lets handle it just incase by returning an error. + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID), + } +} + +func videoImpInsertion(bid *openrtb.Bid) string { + adm := bid.AdM + nurl := bid.NURL + search := "" + imp := "" + wrapped_nurl := fmt.Sprintf(imp, nurl) + results := strings.Replace(adm, search, wrapped_nurl, 1) + return results +} + +func isDmxParams(t interface{}) bool { + switch t.(type) { + case dmxParams: + return true + default: + return false + } +} diff --git a/adapters/dmx/dmx_test.go b/adapters/dmx/dmx_test.go new file mode 100644 index 00000000000..e9f195eb61d --- /dev/null +++ b/adapters/dmx/dmx_test.go @@ -0,0 +1,782 @@ +package dmx + +import ( + "encoding/json" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "strings" + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +var ( + bidRequest string +) + +func TestFetchParams(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + var arrImp []openrtb.Imp + var imps = fetchParams( + dmxExt{Bidder: dmxParams{ + TagId: "222", + PublisherId: "5555", + }}, + openrtb.Imp{ID: "32"}, + openrtb.Imp{ID: "32"}, + arrImp, + &openrtb.Banner{W: &width, H: &height, Format: []openrtb.Format{ + {W: 300, H: 250}, + }}, + nil, + 1) + var imps2 = fetchParams( + dmxExt{Bidder: dmxParams{ + DmxId: "222", + MemberId: "5555", + }}, + openrtb.Imp{ID: "32"}, + openrtb.Imp{ID: "32"}, + arrImp, + &openrtb.Banner{W: &width, H: &height, Format: []openrtb.Format{ + {W: 300, H: 250}, + }}, + nil, + 1) + if len(imps) == 0 { + t.Errorf("should increment the length by one") + } + + if len(imps2) == 0 { + t.Errorf("should increment the length by one") + } + +} +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "dmxtest", new(DmxAdapter)) +} + +func TestMakeRequestsOtherPlacement(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + User: &openrtb.User{ID: "bscakucbkasucbkasunscancasuin"}, + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + ID: "1234", + } + + actualAdapterRequests, err := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if actualAdapterRequests == nil { + t.Errorf("request should be nil") + } + if len(err) != 0 { + t.Errorf("We should have no error") + } + +} + +func TestMakeRequestsInvalid(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + ID: "1234", + } + + actualAdapterRequests, err := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if len(actualAdapterRequests) != 0 { + t.Errorf("request should be nil") + } + if len(err) == 0 { + t.Errorf("We should have no error") + } + +} + +func TestMakeRequestNoSite(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + App: &openrtb.App{ID: "cansanuabnua", Publisher: &openrtb.Publisher{ID: "whatever"}}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if len(actualAdapterRequests) != 1 { + t.Errorf("openrtb type should be an Array when it's an App") + } + var the_body openrtb.BidRequest + if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil { + t.Errorf("failed to read bid request") + } + + if the_body.App == nil { + t.Errorf("app property should be populated") + } + + if the_body.App.Publisher.ID == "" { + t.Errorf("Missing publisher ID must be in") + } +} + +func TestMakeRequestsApp(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + App: &openrtb.App{ID: "cansanuabnua", Publisher: &openrtb.Publisher{ID: "whatever"}}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if len(actualAdapterRequests) != 1 { + t.Errorf("openrtb type should be an Array when it's an App") + } + var the_body openrtb.BidRequest + if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil { + t.Errorf("failed to read bid request") + } + + if the_body.App == nil { + t.Errorf("app property should be populated") + } + +} + +func TestMakeRequestsNoUser(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if actualAdapterRequests != nil { + t.Errorf("openrtb type should be empty") + } + +} + +func TestMakeRequests(t *testing.T) { + //server := httptest.NewServer(http.HandlerFunc(DummyDmxServer)) + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + imp2 := openrtb.Imp{ + ID: "imp2", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + imp3 := openrtb.Imp{ + ID: "imp3", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1, imp2, imp3}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{ID: "districtmID"}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if len(actualAdapterRequests) != 1 { + t.Errorf("should have 1 request") + } + var the_body openrtb.BidRequest + if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil { + t.Errorf("failed to read bid request") + } + + if len(the_body.Imp) != 3 { + t.Errorf("must have 3 bids") + } + +} + +func TestMakeBidVideo(t *testing.T) { + var w, h int = 640, 480 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Video: &openrtb.Video{ + W: width, + H: height, + MIMEs: []string{"video/mp4"}, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{ID: "districtmID"}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + if len(actualAdapterRequests) != 1 { + t.Errorf("should have 1 request") + } + var the_body openrtb.BidRequest + if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil { + t.Errorf("failed to read bid request") + } + + if len(the_body.Imp) != 1 { + t.Errorf("must have 1 bids") + } +} + +func TestMakeBidsNoContent(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{ID: "districtmID"}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + + _, err204 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 204}) + + if err204 != nil { + t.Errorf("Was expecting nil") + } + + _, err400 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 400}) + + if err400 == nil { + t.Errorf("Was expecting error") + } + + _, err500 := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], &adapters.ResponseData{StatusCode: 500}) + + if err500 == nil { + t.Errorf("Was expecting error") + } + + bidResponse := &adapters.ResponseData{ + StatusCode: 200, + Body: []byte(`{ + "id": "JdSgvXjee0UZ", + "seatbid": [ + { + "bid": [ + { + "id": "16-40dbf1ef_0gKywr9JnzPAW4bE-1", + "impid": "imp1", + "price": 2.3456, + "adm": "", + "nurl": "dmxnotificationurlhere", + "adomain": [ + "brand.com", + "advertiser.net" + ], + "cid": "12345", + "crid": "232303", + "cat": [ + "IAB20-3" + ], + "attr": [ + 2 + ], + "w": 300, + "h": 600, + "language": "en" + } + ], + "seat": "10001" + } + ], + "cur": "USD" +}`), + } + + bidResponseNoMatch := &adapters.ResponseData{ + StatusCode: 200, + Body: []byte(`{ + "id": "JdSgvXjee0UZ", + "seatbid": [ + { + "bid": [ + { + "id": "16-40dbf1ef_0gKywr9JnzPAW4bE-1", + "impid": "djvnsvns", + "price": 2.3456, + "adm": "", + "nurl": "dmxnotificationurlhere", + "adomain": [ + "brand.com", + "advertiser.net" + ], + "cid": "12345", + "crid": "232303", + "cat": [ + "IAB20-3" + ], + "attr": [ + 2 + ], + "w": 300, + "h": 600, + "language": "en" + } + ], + "seat": "10001" + } + ], + "cur": "USD" +}`), + } + + bids, _ := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], bidResponse) + if bids == nil { + t.Errorf("ads not parse") + } + bidsNoMatching, _ := adapter.MakeBids(&inputRequest, actualAdapterRequests[0], bidResponseNoMatch) + if bidsNoMatching == nil { + t.Errorf("ads not parse") + } + +} +func TestUserExtEmptyObject(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1, imp1, imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{Ext: json.RawMessage(`{}`)}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + if len(actualAdapterRequests) != 0 { + t.Errorf("should have 0 request") + } +} +func TestUserEidsOnly(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1, imp1, imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{Ext: json.RawMessage(`{"eids": [{ + "source": "adserver.org", + "uids": [{ + "id": "111111111111", + "ext": { + "rtiPartner": "TDID" + } + }] + },{ + "source": "netid.de", + "uids": [{ + "id": "11111111" + }] + }] + }`)}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + if len(actualAdapterRequests) != 1 { + t.Errorf("should have 1 request") + } +} + +func TestUserDigitrustOnly(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1, imp1, imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{Ext: json.RawMessage(`{ + "digitrust": { + "id": "11111111111", + "keyv": 4 + }}`)}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + if len(actualAdapterRequests) != 1 { + t.Errorf("should have 1 request") + } +} + +func TestUsersEids(t *testing.T) { + var w, h int = 300, 250 + + var width, height uint64 = uint64(w), uint64(h) + + adapter := NewDmxBidder("https://dmx.districtm.io/b/v2") + imp1 := openrtb.Imp{ + ID: "imp1", + Ext: json.RawMessage("{\"bidder\":{\"dmxid\": \"1007\", \"memberid\": \"123456\", \"seller_id\":\"1008\"}}"), + Banner: &openrtb.Banner{ + W: &width, + H: &height, + Format: []openrtb.Format{ + {W: 300, H: 250}, + }, + }} + + inputRequest := openrtb.BidRequest{ + Imp: []openrtb.Imp{imp1, imp1, imp1}, + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "10007", + }, + }, + User: &openrtb.User{ID: "districtmID", Ext: json.RawMessage(`{"eids": [{ + "source": "adserver.org", + "uids": [{ + "id": "111111111111", + "ext": { + "rtiPartner": "TDID" + } + }] + },{ + "source": "pubcid.org", + "uids": [{ + "id":"11111111" + }] + }, + { + "source": "id5-sync.com", + "uids": [{ + "id": "ID5-12345" + }] + }, + { + "source": "parrable.com", + "uids": [{ + "id": "01.1563917337.test-eid" + }] + },{ + "source": "identityLink", + "uids": [{ + "id": "11111111" + }] + },{ + "source": "criteo", + "uids": [{ + "id": "11111111" + }] + },{ + "source": "britepool.com", + "uids": [{ + "id": "11111111" + }] + },{ + "source": "liveintent.com", + "uids": [{ + "id": "11111111" + }] + },{ + "source": "netid.de", + "uids": [{ + "id": "11111111" + }] + }], + "digitrust": { + "id": "11111111111", + "keyv": 4 + }}`)}, + ID: "1234", + } + + actualAdapterRequests, _ := adapter.MakeRequests(&inputRequest, &adapters.ExtraRequestInfo{}) + if len(actualAdapterRequests) != 1 { + t.Errorf("should have 1 request") + } + var the_body openrtb.BidRequest + if err := json.Unmarshal(actualAdapterRequests[0].Body, &the_body); err != nil { + t.Errorf("failed to read bid request") + } + + if len(the_body.Imp) != 3 { + t.Errorf("must have 3 bids") + } +} +func TestVideoImpInsertion(t *testing.T) { + var bidResp openrtb.BidResponse + var bid openrtb.Bid + payload := []byte(`{ + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "video1", + "impid": "video1", + "price": 5.01, + "nurl": "https://demo.arripiblik.com/359585167267151", + "adm": "BidSwitch", + "crid": "76575664756", + "dealid": "dmx-deal-hp-24", + "w": 640, + "h": 480, + "ext": { + "prebid": { + "type": "video" + } + } + }, + { + "id": "some-impression-id", + "impid": "some-impression-id", + "price": 5.01, + "adm": "", + "crid": "1346943998", + "dealid": "dmx-deal-hp-24", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + { + "id": "some-impression-id2", + "impid": "some-impression-id2", + "price": 5.01, + "adm": "", + "crid": "1424798162", + "dealid": "dmx-deal-hp-24", + "w": 728, + "h": 90, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "dmx" + } + ] +}`) + + err := json.Unmarshal(payload, &bidResp) + if err != nil { + t.Errorf("Payload is invalid") + } + bid = openrtb.Bid(bidResp.SeatBid[0].Bid[0]) + data := videoImpInsertion(&bid) + find := strings.Index(data, "demo.arripiblik.com") + if find == -1 { + t.Errorf("String was not found") + } + +} diff --git a/adapters/dmx/dmxtest/exemplary/simple-app.json b/adapters/dmx/dmxtest/exemplary/simple-app.json new file mode 100644 index 00000000000..a2d57163f3c --- /dev/null +++ b/adapters/dmx/dmxtest/exemplary/simple-app.json @@ -0,0 +1,138 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app":{ + "bundle":"302324249", + "id":"ed6207cefff74c14878963566683c070", + "name":"Skout - iOS Match Buy", + "publisher":{ + "id":"10400" + }, + "storeurl":"https://itunes.apple.com/app/id302324249" + }, + "imp": [ + { + + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250, + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "dmxid": "123454", + "publisher_id": "10400" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "", + "body": { + "id": "test-request-id", + "app":{ + "bundle":"302324249", + "id":"ed6207cefff74c14878963566683c070", + "name":"Skout - iOS Match Buy", + "publisher":{ + "id":"10400" + }, + "storeurl":"https://itunes.apple.com/app/id302324249" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "123454", + "secure": 1, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisher_id": "10400", + "dmxid": "123454" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adid": "29681110", + "adm": "
banner-ads
", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adm": "
banner-ads
", + "adid": "29681110", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250 + + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/dmx/dmxtest/exemplary/simple-banner.json b/adapters/dmx/dmxtest/exemplary/simple-banner.json new file mode 100644 index 00000000000..03ea6246ee4 --- /dev/null +++ b/adapters/dmx/dmxtest/exemplary/simple-banner.json @@ -0,0 +1,126 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "fhacacnasicnaic" + }, + "imp": [ + { + + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250, + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "dmxid": "123454", + "publisher_id": "10400" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "", + "body": { + "id": "test-request-id", + "user": { + "id": "fhacacnasicnaic" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "123454", + "secure": 1, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisher_id": "10400", + "dmxid": "123454" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adid": "29681110", + "adm": "
banner-ads
", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adm": "
banner-ads
", + "adid": "29681110", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250 + + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/dmx/dmxtest/exemplary/simple-video.json b/adapters/dmx/dmxtest/exemplary/simple-video.json new file mode 100644 index 00000000000..b4c53188119 --- /dev/null +++ b/adapters/dmx/dmxtest/exemplary/simple-video.json @@ -0,0 +1,112 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "whateveryouwant" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "minduration": 15, + "maxduration": 30, + "protocols": [2, 3, 5, 6, 7, 8], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "tagid": "12345", + "publisher_id": "10400" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "", + "body": { + "id": "test-request-id", + "user": { + "id": "whateveryouwant" + }, + "imp": [ + { + "ext": { + "bidder": { + "tagid": "12345", + "publisher_id": "10400" + } + }, + "id": "test-imp-id", + "tagid": "12345", + "secure": 1, + "video": { + "mimes": ["video/mp4"], + "minduration": 15, + "maxduration": 30, + "protocols": [2, 3, 5, 6, 7, 8], + "w": 940, + "h": 560 + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.90, + "adid": "29681110", + "adm": "ads", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.90, + "adm": "ads", + "adid": "29681110", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/dmx/dmxtest/params/race/banner.json b/adapters/dmx/dmxtest/params/race/banner.json new file mode 100644 index 00000000000..1c0adff78ac --- /dev/null +++ b/adapters/dmx/dmxtest/params/race/banner.json @@ -0,0 +1,4 @@ +{ + "tagid": "25251", + "publisher_id": "100152" +} \ No newline at end of file diff --git a/adapters/dmx/dmxtest/params/race/video.json b/adapters/dmx/dmxtest/params/race/video.json new file mode 100644 index 00000000000..3bbd83bd3b0 --- /dev/null +++ b/adapters/dmx/dmxtest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "tagid": "25255", + "publisher_id": "100151" +} \ No newline at end of file diff --git a/adapters/dmx/usersync.go b/adapters/dmx/usersync.go new file mode 100644 index 00000000000..98e56234fa6 --- /dev/null +++ b/adapters/dmx/usersync.go @@ -0,0 +1,12 @@ +package dmx + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewDmxSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("dmx", 144, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/dmx/usersync_test.go b/adapters/dmx/usersync_test.go new file mode 100644 index 00000000000..e4e3c7d8e55 --- /dev/null +++ b/adapters/dmx/usersync_test.go @@ -0,0 +1,20 @@ +package dmx + +import ( + "github.com/prebid/prebid-server/privacy" + "testing" + "text/template" + + "github.com/stretchr/testify/assert" +) + +func TestDmxSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("https://dmx.districtm.io/s/v1/img/s/10007")) + syncer := NewDmxSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{}) + assert.NoError(t, err) + assert.Equal(t, "https://dmx.districtm.io/s/v1/img/s/10007", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 144, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 9652ae141f5..e93aed46eab 100755 --- a/config/config.go +++ b/config/config.go @@ -534,6 +534,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDmx, "https://dmx.districtm.io/s/v1/img/s/10007?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D%24%7Bgdpr%7D%26gdpr_consent%3D%24%7Bgdpr_consent%7D%26uid%3D%24%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dengagebdr%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEPlanning, "https://ads.us.e-planning.net/uspd/1/?du="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -711,9 +712,10 @@ func SetupViper(v *viper.Viper, filename string) { // for them and specify all the parameters they need for them to work correctly. v.SetDefault("adapters.audiencenetwork.disabled", true) v.SetDefault("adapters.rubicon.disabled", true) - v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb") v.SetDefault("adapters.33across.partner_id", "") + v.SetDefault("adapters.dmx.endpoint", "https://dmx.districtm.io/b/v2") + v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx") v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1") v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index b69b5b50e13..390016117fb 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -29,6 +29,7 @@ import ( "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/cpmstar" "github.com/prebid/prebid-server/adapters/datablocks" + "github.com/prebid/prebid-server/adapters/dmx" "github.com/prebid/prebid-server/adapters/emx_digital" "github.com/prebid/prebid-server/adapters/engagebdr" "github.com/prebid/prebid-server/adapters/eplanning" @@ -107,6 +108,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), + openrtb_ext.BidderDmx: dmx.NewDmxBidder(cfg.Adapters[string(openrtb_ext.BidderDmx)].Endpoint), openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 8a53e4adcf2..b3ecddb06cd 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -46,6 +46,7 @@ const ( BidderConversant BidderName = "conversant" BidderCpmstar BidderName = "cpmstar" BidderDatablocks BidderName = "datablocks" + BidderDmx BidderName = "dmx" BidderEmxDigital BidderName = "emx_digital" BidderEngageBDR BidderName = "engagebdr" BidderEPlanning BidderName = "eplanning" @@ -122,6 +123,7 @@ var BidderMap = map[string]BidderName{ "conversant": BidderConversant, "cpmstar": BidderCpmstar, "datablocks": BidderDatablocks, + "dmx": BidderDmx, "emx_digital": BidderEmxDigital, "engagebdr": BidderEngageBDR, "eplanning": BidderEPlanning, diff --git a/static/bidder-info/dmx.yaml b/static/bidder-info/dmx.yaml new file mode 100644 index 00000000000..d6e54178db4 --- /dev/null +++ b/static/bidder-info/dmx.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "steve@districtm.net" +capabilities: + site: + mediaTypes: + - banner + - video + app: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/dmx.json b/static/bidder-params/dmx.json new file mode 100644 index 00000000000..4c0df65e3d4 --- /dev/null +++ b/static/bidder-params/dmx.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "District M DMX Adapter Params", + "description": "A schema which validates params accepted by the DMX adapter", + "type": "object", + "properties": { + "memberid" : { + "type": "string", + "description": "Represent boost MemberId from districtm UI" + }, + "tagid": { + "type": "string", + "description": "Represent the placement ID, this value is optional" + }, + "bidfloor": { + "type": "string", + "description": "The minimum price acceptable for a bid" + } + }, + + "required": ["memberid"] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 791a00de0a9..5dccf855add 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -24,6 +24,7 @@ import ( "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/cpmstar" "github.com/prebid/prebid-server/adapters/datablocks" + "github.com/prebid/prebid-server/adapters/dmx" "github.com/prebid/prebid-server/adapters/emx_digital" "github.com/prebid/prebid-server/adapters/engagebdr" "github.com/prebid/prebid-server/adapters/eplanning" @@ -94,6 +95,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderCpmstar, cpmstar.NewCpmstarSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderDatablocks, datablocks.NewDatablocksSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderDmx, dmx.NewDmxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderEmxDigital, emx_digital.NewEMXDigitalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderEPlanning, eplanning.NewEPlanningSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 9aae284da2a..ddd067e8be7 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -32,6 +32,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderConversant): syncConfig, string(openrtb_ext.BidderCpmstar): syncConfig, string(openrtb_ext.BidderDatablocks): syncConfig, + string(openrtb_ext.BidderDmx): syncConfig, string(openrtb_ext.BidderEmxDigital): syncConfig, string(openrtb_ext.BidderEngageBDR): syncConfig, string(openrtb_ext.BidderEPlanning): syncConfig, From b10b55ce107f1f217b5476367129aaf528350990 Mon Sep 17 00:00:00 2001 From: hbanalytics <55453525+hbanalytics@users.noreply.github.com> Date: Thu, 4 Jun 2020 20:18:34 +0300 Subject: [PATCH 106/318] Fix sync url for Yieldone s2s Bid Adapter (#1336) * Fix typo in Yieldone sync url --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index e93aed46eab..4e54bc712a2 100755 --- a/config/config.go +++ b/config/config.go @@ -575,7 +575,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderVrtcal doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldlab, "https://ad.yieldlab.net/mr?t=2&pid=9140838&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldlab%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25YL_UID%25%25") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldmo, "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldmo%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") - setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldone, "https://y.one.impact-ad.jp/hbs_sc?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderYieldone, "https://y.one.impact-ad.jp/hbs_cs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dyieldone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderZeroClickFraud, "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") } From dc9d246285fea7b5fe13ebdb4a5e2992b0cbbead Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 8 Jun 2020 17:34:21 -0400 Subject: [PATCH 107/318] CCPA Video Bug (#1333) --- endpoints/openrtb2/amp_auction_test.go | 3 +- endpoints/openrtb2/auction.go | 7 +- endpoints/openrtb2/auction_test.go | 12 +- .../video/video_invalid_sample.json | 125 +++++++------- .../video/video_valid_sample.json | 155 ++++++++--------- .../video_valid_sample_ccpa_malformed.json | 88 ++++++++++ .../video/video_valid_sample_ccpa_valid.json | 88 ++++++++++ ...ideo_valid_sample_different_durations.json | 159 +++++++++--------- ...o_valid_sample_with_device_user_agent.json | 155 ++++++++--------- ...alid_sample_without_device_user_agent.json | 122 +++++++------- endpoints/openrtb2/video_auction.go | 4 +- endpoints/openrtb2/video_auction_test.go | 101 ++++++++++- privacy/ccpa/policy.go | 37 +++- privacy/ccpa/policy_test.go | 42 +++++ 14 files changed, 729 insertions(+), 369 deletions(-) create mode 100644 endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_malformed.json create mode 100644 endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_valid.json diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 9dc81eb1b9d..289db3f48cb 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -122,9 +122,8 @@ func TestAMPPageInfo(t *testing.T) { } func TestGDPRConsent(t *testing.T) { - consent := "BONV8oqONXwgmADACHENAO7pqzAAppY" + consent := "BOu5On0Ou5On0ADACHENAO7pqzAAppY" existingConsent := "BONV8oqONXwgmADACHENAO7pqzAAppY" - digitrust := &openrtb_ext.ExtUserDigiTrust{ ID: "anyDigitrustID", KeyV: 1, diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index bcb13724519..bd50fca9149 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -315,7 +315,12 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { } if err := ccpaPolicy.Validate(); err != nil { - errL = append(errL, &errortypes.Warning{Message: fmt.Sprintf("CCPA value is invalid and will be ignored. (%s)", err.Error())}) + errL = append(errL, &errortypes.InvalidPrivacyConsent{Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err)}) + + ccpaPolicy.Value = "" + if err := ccpaPolicy.Write(req); err != nil { + errL = append(errL, fmt.Errorf("Unable to remove invalid CCPA consent from the request. (%v)", err)) + } } impIDs := make(map[string]int, len(req.Imp)) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index fdd6b3a47cf..c3b9267bf8b 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -915,7 +915,7 @@ func TestCurrencyTrunc(t *testing.T) { assert.ElementsMatch(t, errL, []error{&expectedError}) } -func TestCCPAInvalidValueWarning(t *testing.T) { +func TestCCPAInvalid(t *testing.T) { deps := &endpointDeps{ &nobidExchange{}, newParamsValidator(t), @@ -943,21 +943,23 @@ func TestCCPAInvalidValueWarning(t *testing.T) { W: &ui, H: &ui, }, - Ext: json.RawMessage("{\"appnexus\": {\"placementId\": 5667}}"), + Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`), }, }, Site: &openrtb.Site{ ID: "myID", }, Regs: &openrtb.Regs{ - Ext: json.RawMessage("{\"us_privacy\":\"invalid by length\"}"), + Ext: json.RawMessage(`{"us_privacy":"invalid by length"}`), }, } errL := deps.validateRequest(&req) - expectedError := errortypes.Warning{Message: "CCPA value is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)"} - assert.ElementsMatch(t, errL, []error{&expectedError}) + expectedWarning := errortypes.InvalidPrivacyConsent{Message: "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)"} + assert.ElementsMatch(t, errL, []error{&expectedWarning}) + + assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request") } // nobidExchange is a well-behaved exchange which always bids "no bid". diff --git a/endpoints/openrtb2/sample-requests/video/video_invalid_sample.json b/endpoints/openrtb2/sample-requests/video/video_invalid_sample.json index 0a9fe656362..d62f40438b4 100644 --- a/endpoints/openrtb2/sample-requests/video/video_invalid_sample.json +++ b/endpoints/openrtb2/sample-requests/video/video_invalid_sample.json @@ -1,68 +1,69 @@ { - "description": "Video endpoint valid request.", + "description": "Video endpoint valid request due to missing pods.", - "requestPayload": -{ - "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", - "accountid": "555888777", - - "site": { - "page": "prebid.com" - }, - "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" + "requestPayload": { + "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "user": { + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } + } + }, + "device": { + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] }, - "gdpr": { - "consentrequired": false, - "consentstring": "something" + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 }, - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling" - }, - "device11": { - "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", - "ip": "123.145.167.10", - "devicetype": 1, - "dnt": 33, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "lmt": 44, - "os": "mac os", - "w": 640, - "h": 480, - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" - }, - "includebrandcategory":{ - "primaryadserver": 1, - "publisher": "" - }, - "video": { - "w": 640, - "h": 480, - "mimes": [ - "video/mp4" - ], - "protocols": [ - 2,3,5,6 - ] - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "livestream": 0 - }, - "cacheconfig": { - "ttl": 42 + "cacheconfig": { + "ttl": 42 + } } -} } \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample.json index caa16f523dc..7ccdbf83a46 100644 --- a/endpoints/openrtb2/sample-requests/video/video_valid_sample.json +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample.json @@ -1,85 +1,86 @@ { "description": "Video endpoint valid request.", - "requestPayload": -{ - "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", - "accountid": "555888777", - "podconfig": { - "durationrangesec": [ - 30 - ], - "requireexactduration": true, - "pods": [ - { - "podid": 1, - "adpoddurationsec": 180, - "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" - }, - { - "podid": 2, - "adpoddurationsec": 150, - "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + "requestPayload": { + "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "user": { + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } } - ] - }, - "site": { - "page": "prebid.com" - }, - "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" }, - "gdpr": { - "consentrequired": false, - "consentstring": "something" + "device": { + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 }, - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling" - }, - "device11": { - "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", - "ip": "123.145.167.10", - "devicetype": 1, - "dnt": 33, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "lmt": 44, - "os": "mac os", - "w": 640, - "h": 480, - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" - }, - "includebrandcategory":{ - "primaryadserver": 1, - "publisher": "" - }, - "video": { - "w": 640, - "h": 480, - "mimes": [ - "video/mp4" - ], - "protocols": [ - 2,3,5,6 - ] - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "livestream": 0 - }, - "cacheconfig": { - "ttl": 42 + "cacheconfig": { + "ttl": 42 + } } -} } \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_malformed.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_malformed.json new file mode 100644 index 00000000000..b512c68346e --- /dev/null +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_malformed.json @@ -0,0 +1,88 @@ +{ + "description": "Video endpoint valid request.", + + "requestPayload": { + "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "${malformed}" + } + }, + "user": { + "buyeruid": "anyId", + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } + } + }, + "device": { + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 + }, + "cacheconfig": { + "ttl": 42 + } + } +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_valid.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_valid.json new file mode 100644 index 00000000000..cfa389d4ce2 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_ccpa_valid.json @@ -0,0 +1,88 @@ +{ + "description": "Video endpoint valid request.", + + "requestPayload": { + "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "1NYN" + } + }, + "user": { + "buyeruid": "anyId", + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } + } + }, + "device": { + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 + }, + "cacheconfig": { + "ttl": 42 + } + } +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_different_durations.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_different_durations.json index 504af2d61cd..c3ad776960a 100644 --- a/endpoints/openrtb2/sample-requests/video/video_valid_sample_different_durations.json +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_different_durations.json @@ -1,86 +1,87 @@ { - "description": "Video endpoint valid request.", + "description": "Video endpoint valid request with different durations.", - "requestPayload": -{ - "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", - "accountid": "555888777", - "podconfig": { - "durationrangesec": [ - 15, - 30 - ], - "requireexactduration": true, - "pods": [ - { - "podid": 1, - "adpoddurationsec": 180, - "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" - }, - { - "podid": 2, - "adpoddurationsec": 150, - "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + "requestPayload": { + "storedrequestid": "80ce30c53c16e6ede735f123ef6e32361bfc7b22", + "podconfig": { + "durationrangesec": [ + 15, + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "user": { + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } } - ] - }, - "site": { - "page": "prebid.com" - }, - "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" }, - "gdpr": { - "consentrequired": false, - "consentstring": "something" + "device": { + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 }, - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling" - }, - "device11": { - "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", - "ip": "123.145.167.10", - "devicetype": 1, - "dnt": 33, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "lmt": 44, - "os": "mac os", - "w": 640, - "h": 480, - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" - }, - "includebrandcategory":{ - "primaryadserver": 1, - "publisher": "" - }, - "video": { - "w": 640, - "h": 480, - "mimes": [ - "video/mp4" - ], - "protocols": [ - 2,3,5,6 - ] - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "livestream": 0 - }, - "cacheconfig": { - "ttl": 42 + "cacheconfig": { + "ttl": 42 + } } -} } \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json index 68c3f4e1c15..6a9dc605ea2 100644 --- a/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_with_device_user_agent.json @@ -1,80 +1,85 @@ - { - "accountid": "555888777", - "podconfig": { - "durationrangesec": [ - 30 - ], - "requireexactduration": true, - "pods": [ - { - "podid": 1, - "adpoddurationsec": 180, - "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" - }, - { - "podid": 2, - "adpoddurationsec": 150, - "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + "description": "Video endpoint valid request with device data.", + + "requestPayload": { + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0 } - ] - }, - "site": { - "page": "prebid.com" - }, - "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" }, - "gdpr": { - "consentrequired": false, - "consentstring": "something" + "user": { + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } + } + }, + "device": { + "ua": "TestHeaderSample", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", + "lmt": 44, + "os": "mac os", + "w": 640, + "h": 480, + "didsha1": "didsha1", + "didmd5": "didmd5", + "dpidsha1": "dpidsha1", + "dpidmd5": "dpidmd5", + "macsha1": "macsha1", + "macmd5": "macmd5" + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 }, - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling" - }, - "device": { - "ua": "TestHeaderSample", - "ip": "123.145.167.10", - "devicetype": 1, - "dnt": 33, - "ifa": "AA000DFE74168477C70D291f574D344790E0BB11", - "lmt": 44, - "os": "mac os", - "w": 640, - "h": 480, - "didsha1": "didsha1", - "didmd5": "didmd5", - "dpidsha1": "dpidsha1", - "dpidmd5": "dpidmd5", - "macsha1": "macsha1", - "macmd5": "macmd5" - }, - "includebrandcategory":{ - "primaryadserver": 1, - "publisher": "" - }, - "video": { - "w": 640, - "h": 480, - "mimes": [ - "video/mp4" - ], - "protocols": [ - 2,3,5,6 - ] - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "livestream": 0 - }, - "cacheconfig": { - "ttl": 42 + "cacheconfig": { + "ttl": 42 + } } -} +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json b/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json index e040a5625ba..199391865b2 100644 --- a/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json +++ b/endpoints/openrtb2/sample-requests/video/video_valid_sample_without_device_user_agent.json @@ -1,63 +1,69 @@ - { - "accountid": "555888777", - "podconfig": { - "durationrangesec": [ - 30 - ], - "requireexactduration": true, - "pods": [ - { - "podid": 1, - "adpoddurationsec": 180, - "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" - }, - { - "podid": 2, - "adpoddurationsec": 150, - "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + "description": "Video endpoint valid request without device data.", + + "requestPayload": { + "podconfig": { + "durationrangesec": [ + 30 + ], + "requireexactduration": true, + "pods": [{ + "podid": 1, + "adpoddurationsec": 180, + "configid": "fba10607-0c12-43d1-ad07-b8a513bc75d6" + }, + { + "podid": 2, + "adpoddurationsec": 150, + "configid": "8b452b41-2681-4a20-9086-6f16ffad7773" + } + ] + }, + "site": { + "page": "prebid.com" + }, + "regs": { + "ext": { + "gdpr": 0 } - ] - }, - "site": { - "page": "prebid.com" - }, - "user": { - "buyeruids": { - "appnexus": "unique_id_an", - "rubicon": "unique_id_rubi" }, - "gdpr": { - "consentrequired": false, - "consentstring": "something" + "user": { + "yob": 1991, + "gender": "F", + "keywords": "Hotels, Travelling", + "ext": { + "prebid": { + "buyeruids": { + "appnexus": "unique_id_an", + "rubicon": "unique_id_rubi" + } + } + } + }, + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "" + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, 3, 5, 6 + ] + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "livestream": 0 }, - "yob": 1991, - "gender": "F", - "keywords": "Hotels, Travelling" - }, - "includebrandcategory":{ - "primaryadserver": 1, - "publisher": "" - }, - "video": { - "w": 640, - "h": 480, - "mimes": [ - "video/mp4" - ], - "protocols": [ - 2,3,5,6 - ] - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "livestream": 0 - }, - "cacheconfig": { - "ttl": 42 + "cacheconfig": { + "ttl": 42 + } } -} +} \ No newline at end of file diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 020a5196333..64c99fa5a3e 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -204,7 +204,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re deps.setFieldsImplicitly(r, bidReq) // move after merge errL = deps.validateRequest(bidReq) - if len(errL) > 0 { + if errortypes.ContainsFatalError(errL) { handleError(&labels, w, errL, &vo, &debugLog) return } @@ -232,7 +232,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { - errL = append(errL, acctIdErr) + errL := []error{err} handleError(&labels, w, errL, &vo, &debugLog) return } diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 5ba34068f7b..631cb277f7f 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -1,6 +1,7 @@ package openrtb2 import ( + "bytes" "context" "encoding/json" "errors" @@ -860,12 +861,12 @@ func TestParseVideoRequestWithUserAgentAndHeader(t *testing.T) { if err != nil { t.Fatalf("Failed to fetch a valid request: %v", err) } - headers := http.Header{} headers.Add("User-Agent", "TestHeader") deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -883,7 +884,8 @@ func TestParseVideoRequestWithUserAgentAndEmptyHeader(t *testing.T) { headers := http.Header{} deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, "TestHeaderSample", req.Device.UA, "Header should be taken from original request") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -902,7 +904,8 @@ func TestParseVideoRequestWithoutUserAgentWithHeader(t *testing.T) { headers.Add("User-Agent", "TestHeader") deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, "TestHeader", req.Device.UA, "Device.ua should be taken from request header") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -920,7 +923,8 @@ func TestParseVideoRequestWithoutUserAgentAndEmptyHeader(t *testing.T) { headers := http.Header{} deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, "", req.Device.UA, "Device.ua should be empty") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -942,7 +946,8 @@ func TestParseVideoRequestWithEncodedUserAgentInHeader(t *testing.T) { headers.Add("User-Agent", uaEncoded) deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -963,7 +968,8 @@ func TestParseVideoRequestWithDecodedUserAgentInHeader(t *testing.T) { headers.Add("User-Agent", uaDecoded) deps := mockDeps(t, ex) - req, valErr, podErr := deps.parseVideoRequest(reqData, headers) + reqBody := string(getRequestPayload(t, reqData)) + req, valErr, podErr := deps.parseVideoRequest([]byte(reqBody), headers) assert.Equal(t, uaDecoded, req.Device.UA, "Device.ua should be taken from request header") assert.Equal(t, []error(nil), valErr, "No validation errors should be returned") @@ -1036,6 +1042,71 @@ func TestCreateImpressionTemplate(t *testing.T) { assert.Equal(t, res.Video.PlaybackMethod, []openrtb.PlaybackMethod{7, 8}, "Incorrect video playback method") } +func TestCCPA(t *testing.T) { + testCases := []struct { + description string + testFilePath string + expectConsentString bool + }{ + { + description: "Missing Consent", + testFilePath: "sample-requests/video/video_valid_sample.json", + expectConsentString: false, + }, + { + description: "Valid Consent", + testFilePath: "sample-requests/video/video_valid_sample_ccpa_valid.json", + expectConsentString: true, + }, + { + description: "Malformed Consent", + testFilePath: "sample-requests/video/video_valid_sample_ccpa_malformed.json", + expectConsentString: false, + }, + } + + for _, test := range testCases { + // Load Test Request + requestContainerBytes, err := ioutil.ReadFile(test.testFilePath) + if err != nil { + t.Fatalf("%s: Failed to fetch a valid request: %v", test.description, err) + } + requestBytes := getRequestPayload(t, requestContainerBytes) + + // Create HTTP Request + Response Recorder + httpRequest := httptest.NewRequest("POST", "/openrtb2/video", bytes.NewReader(requestBytes)) + httpResponseRecorder := httptest.NewRecorder() + + // Run Test + ex := &mockExchangeVideo{} + mockDeps(t, ex).VideoAuctionEndpoint(httpResponseRecorder, httpRequest, nil) + + // Validate Request To Exchange + // - An error should never be generated for CCPA problems. + if ex.lastRequest == nil { + t.Fatalf("%s: The request never made it into the exchange.", test.description) + } + extRegs := &openrtb_ext.ExtRegs{} + if err = json.Unmarshal(ex.lastRequest.Regs.Ext, extRegs); err != nil { + t.Fatalf("%s: Failed to unmarshal reg.ext in request to the exchange: %v", test.description, err) + } + if test.expectConsentString { + assert.Len(t, extRegs.USPrivacy, 4, test.description+":consent") + } else { + assert.Empty(t, extRegs.USPrivacy, test.description+":consent") + } + + // Validate HTTP Response + responseBytes := httpResponseRecorder.Body.Bytes() + response := &openrtb_ext.BidResponseVideo{} + if err := json.Unmarshal(responseBytes, response); err != nil { + t.Fatalf("%s: Unable to unmarshal response.", test.description) + } + assert.Len(t, ex.lastRequest.Imp, 11, test.description+":imps") + assert.Len(t, response.AdPods, 5, test.description+":adpods") + } +} + func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} @@ -1166,3 +1237,19 @@ var testVideoStoredImpData = map[string]json.RawMessage{ var testVideoStoredRequestData = map[string]json.RawMessage{ "80ce30c53c16e6ede735f123ef6e32361bfc7b22": json.RawMessage(`{"accountid": "11223344", "site": {"page": "mygame.foo.com"}}`), } + +func loadValidRequest(t *testing.T) *openrtb_ext.BidRequestVideo { + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + + reqBody := getRequestPayload(t, reqData) + + reqVideo := &openrtb_ext.BidRequestVideo{} + if err := json.Unmarshal(reqBody, reqVideo); err != nil { + t.Fatalf("Failed to unmarshal the request: %v", err) + } + + return reqVideo +} diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go index 64579f2a2f6..11ac434595a 100644 --- a/privacy/ccpa/policy.go +++ b/privacy/ccpa/policy.go @@ -14,7 +14,7 @@ type Policy struct { Value string } -// ReadPolicy extracts the CCPA regulation policy from an OpenRTB regs ext. +// ReadPolicy extracts the CCPA regulation policy from an OpenRTB request. func ReadPolicy(req *openrtb.BidRequest) (Policy, error) { policy := Policy{} @@ -32,6 +32,10 @@ func ReadPolicy(req *openrtb.BidRequest) (Policy, error) { // Write mutates an OpenRTB bid request with the context of the CCPA policy. func (p Policy) Write(req *openrtb.BidRequest) error { if p.Value == "" { + return clearPolicy(req) + } + + if req == nil { return nil } @@ -59,6 +63,37 @@ func (p Policy) Write(req *openrtb.BidRequest) error { return err } +func clearPolicy(req *openrtb.BidRequest) error { + if req == nil { + return nil + } + + if req.Regs == nil { + return nil + } + + if len(req.Regs.Ext) == 0 { + return nil + } + + var extMap map[string]interface{} + err := json.Unmarshal(req.Regs.Ext, &extMap) + if err == nil { + delete(extMap, "us_privacy") + if len(extMap) == 0 { + req.Regs.Ext = nil + } else { + ext, err := json.Marshal(extMap) + if err == nil { + req.Regs.Ext = ext + } + return err + } + } + + return err +} + // Validate returns an error if the CCPA policy does not adhere to the IAB spec. func (p Policy) Validate() error { if err := ValidateConsent(p.Value); err != nil { diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go index 740f95a8a6a..e9b4c4525b1 100644 --- a/privacy/ccpa/policy_test.go +++ b/privacy/ccpa/policy_test.go @@ -112,6 +112,48 @@ func TestWrite(t *testing.T) { request: &openrtb.BidRequest{}, expected: &openrtb.BidRequest{}, }, + { + description: "Disabled - Nil Request", + policy: Policy{Value: ""}, + request: nil, + expected: nil, + }, + { + description: "Disabled - Empty Regs.Ext", + policy: Policy{Value: ""}, + request: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, + expected: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, + }, + { + description: "Disabled - Remove From Request", + policy: Policy{Value: ""}, + request: &openrtb.BidRequest{Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"toBeRemoved"}`)}}, + expected: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, + }, + { + description: "Disabled - Remove From Request, Leave Other req Values", + policy: Policy{Value: ""}, + request: &openrtb.BidRequest{Regs: &openrtb.Regs{ + COPPA: 42, + Ext: json.RawMessage(`{"us_privacy":"toBeRemoved"}`)}}, + expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ + COPPA: 42}}, + }, + { + description: "Disabled - Remove From Request, Leave Other req.ext Values", + policy: Policy{Value: ""}, + request: &openrtb.BidRequest{Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"existing":"any","us_privacy":"toBeRemoved"}`)}}, + expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"existing":"any"}`)}}, + }, + { + description: "Enabled - Nil Request", + policy: Policy{Value: "anyValue"}, + request: nil, + expected: nil, + }, { description: "Enabled With Nil Request Regs Object", policy: Policy{Value: "anyValue"}, From 47bed2a1a2ab043113391c2ebe25338ecbd83446 Mon Sep 17 00:00:00 2001 From: Artur Aleksanyan Date: Tue, 9 Jun 2020 18:48:04 +0400 Subject: [PATCH 108/318] Add Pubnative bidder documentation (#1340) --- docs/bidders/pubnative.md | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/bidders/pubnative.md diff --git a/docs/bidders/pubnative.md b/docs/bidders/pubnative.md new file mode 100644 index 00000000000..a25cafe0cd5 --- /dev/null +++ b/docs/bidders/pubnative.md @@ -0,0 +1,62 @@ +# Pubnative Bidder + +## Prerequisite +Before adding PubNative as a new bidder, there are 3 prerequisites: +- As a Publisher, you need to have Prebid Mobile SDK integrated. +- You need a configured Prebid Server (either self-hosted or hosted by 3rd party). +- You need to be integrated with Ad Server SDK (e.g. Mopub) or internal product which communicates with Prebid Mobile SDK. + +Please see [documentation](https://developers.pubnative.net/docs/prebid-adding-pubnative-as-a-bidder) for more info. + +## Configuration + +- bidder should be always set to "pubnative" (`imp.ext.pubnative`) +- zone_id (int) should be always set to 1, unless special use case agreed with our account manager. (`imp.ext.pubnative.zone_id`) +- app_auth_token (string) is unique per publisher app. Please contact our account manager to obtain yours. (`imp.ext.pubnative.app_auth_token`) + +An example is illustrated in a section below. + +## Testing + +Please consult with our Account Manager for testing. +We need to confirm that your ad request is correctly received by our system. + +The following test parameters can be used to verify that Prebid Server is working properly with the +Pubnative adapter. + +The following json can be used to do a request to prebid server for verifying its integration with Pubnative adapter. + +```json +{ + "id": "some-impression-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "pubnative": { + "zone_id": 1, + "app_auth_token": "b620e282f3c74787beedda34336a4821" + } + } + } + ], + "device": { + "os": "android", + "h": 700, + "w": 375 + }, + "tmax": 500, + "test": 1 +} +``` \ No newline at end of file From c628f1a83238d9e0ec5b430dd196d597145e1d11 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:15:01 -0400 Subject: [PATCH 109/318] Timeout notification monitoring and debugging (#1322) --- config/config.go | 31 ++++++++++++++++++++ config/config_test.go | 8 ++++++ config/util/loggers.go | 24 ++++++++++++++++ config/util/loggers_test.go | 32 +++++++++++++++++++++ docs/developers/add-new-bidder.md | 10 +++++++ exchange/adapter_map.go | 6 ++-- exchange/adapter_map_test.go | 5 ++-- exchange/bidder.go | 36 +++++++++++++++++++----- exchange/bidder_test.go | 18 ++++++------ exchange/exchange.go | 2 +- exchange/targeting_test.go | 4 ++- pbsmetrics/config/metrics.go | 11 ++++++++ pbsmetrics/go_metrics.go | 18 ++++++++++++ pbsmetrics/go_metrics_test.go | 3 ++ pbsmetrics/metrics.go | 1 + pbsmetrics/metrics_mock.go | 5 ++++ pbsmetrics/prometheus/prometheus.go | 23 +++++++++++++++ pbsmetrics/prometheus/prometheus_test.go | 21 ++++++++++++++ 18 files changed, 237 insertions(+), 21 deletions(-) create mode 100644 config/util/loggers.go create mode 100644 config/util/loggers_test.go diff --git a/config/config.go b/config/config.go index 4e54bc712a2..559fc5dac19 100755 --- a/config/config.go +++ b/config/config.go @@ -66,6 +66,8 @@ type Configuration struct { PemCertsFile string `mapstructure:"certificates_file"` // Custom headers to handle request timeouts from queueing infrastructure RequestTimeoutHeaders RequestTimeoutHeaders `mapstructure:"request_timeout_headers"` + // Debug/logging flags go here + Debug Debug `mapstructure:"debug"` } const MIN_COOKIE_SIZE_BYTES = 500 @@ -104,6 +106,7 @@ func (cfg *Configuration) validate() configErrors { errs = cfg.GDPR.validate(errs) errs = cfg.CurrencyConverter.validate(errs) errs = validateAdapters(cfg.Adapters, errs) + errs = cfg.Debug.validate(errs) return errs } @@ -450,6 +453,30 @@ type DefReqFiles struct { FileName string `mapstructure:"name"` } +type Debug struct { + TimeoutNotification TimeoutNotification `mapstructure:"timeout_notification"` +} + +func (cfg *Debug) validate(errs configErrors) configErrors { + return cfg.TimeoutNotification.validate(errs) +} + +type TimeoutNotification struct { + // Log timeout notifications in the application log + Log bool `mapstructure:"log"` + // Fraction of notifications to log + SamplingRate float32 `mapstructure:"sampling_rate"` + // Only log failures + FailOnly bool `mapstructure:"fail_only"` +} + +func (cfg *TimeoutNotification) validate(errs configErrors) configErrors { + if cfg.SamplingRate < 0.0 || cfg.SamplingRate > 1.0 { + errs = append(errs, fmt.Errorf("debug.timeout_notification.sampling_rate must be positive and not greater than 1.0. Got %f", cfg.SamplingRate)) + } + return errs +} + // New uses viper to get our server configurations. func New(v *viper.Viper) (*Configuration, error) { var c Configuration @@ -820,6 +847,10 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("request_timeout_headers.request_time_in_queue", "") v.SetDefault("request_timeout_headers.request_timeout_in_queue", "") + v.SetDefault("debug.timeout_notification.log", false) + v.SetDefault("debug.timeout_notification.sampling_rate", 0.0) + v.SetDefault("debug.timeout_notification.fail_only", false) + // Set environment variable support: v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.SetEnvPrefix("PBS") diff --git a/config/config_test.go b/config/config_test.go index 92794d7941e..ee8e68e7025 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -426,6 +426,14 @@ func TestCookieSizeError(t *testing.T) { } } +func TestValidateDebug(t *testing.T) { + cfg := newDefaultConfig(t) + cfg.Debug.TimeoutNotification.SamplingRate = 1.1 + + err := cfg.validate() + assert.NotNil(t, err, "cfg.debug.timeout_notification.sampling_rate should not be allowed to be greater than 1.0, but it was allowed") +} + func newDefaultConfig(t *testing.T) *Configuration { v := viper.New() SetupViper(v, "") diff --git a/config/util/loggers.go b/config/util/loggers.go new file mode 100644 index 00000000000..88702e68763 --- /dev/null +++ b/config/util/loggers.go @@ -0,0 +1,24 @@ +package util + +import ( + "math/rand" +) + +type logMsg func(string, ...interface{}) + +type randomGenerator func() float32 + +// LogRandomSample will log a randam sample of the messages it is sent, based on the chance to log +// chance = 1.0 => always log, +// chance = 0.0 => never log +func LogRandomSample(msg string, logger logMsg, chance float32) { + logRandomSampleImpl(msg, logger, chance, rand.Float32) +} + +func logRandomSampleImpl(msg string, logger logMsg, chance float32, randGenerator randomGenerator) { + if chance < 1.0 && randGenerator() > chance { + // this is the chance we don't log anything + return + } + logger(msg) +} diff --git a/config/util/loggers_test.go b/config/util/loggers_test.go new file mode 100644 index 00000000000..4bfab967ec4 --- /dev/null +++ b/config/util/loggers_test.go @@ -0,0 +1,32 @@ +package util + +import ( + "bytes" + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLogRandomSample(t *testing.T) { + + const expected string = `This is test line 2 +This is test line 3 +` + + myRand := rand.New(rand.NewSource(1337)) + var buf bytes.Buffer + + mylogger := func(msg string, args ...interface{}) { + buf.WriteString(fmt.Sprintf(fmt.Sprintln(msg), args...)) + } + + logRandomSampleImpl("This is test line 1", mylogger, 0.5, myRand.Float32) + logRandomSampleImpl("This is test line 2", mylogger, 0.5, myRand.Float32) + logRandomSampleImpl("This is test line 3", mylogger, 0.5, myRand.Float32) + logRandomSampleImpl("This is test line 4", mylogger, 0.5, myRand.Float32) + logRandomSampleImpl("This is test line 5", mylogger, 0.5, myRand.Float32) + + assert.EqualValues(t, expected, buf.String()) +} diff --git a/docs/developers/add-new-bidder.md b/docs/developers/add-new-bidder.md index e68185fdd1c..d76a1fd2fbf 100644 --- a/docs/developers/add-new-bidder.md +++ b/docs/developers/add-new-bidder.md @@ -46,6 +46,16 @@ If bidder is going to support long form video make sure bidder has: Note: `bid.bidVideo.PrimaryCategory` or `TypedBid.bid.Cat` should be specified. To learn more about IAB categories, please refer to this convenience link (not the final official definition): [IAB categories](https://adtagmacros.com/list-of-iab-categories-for-advertisement/) +### Timeout notification support +This is an optional feature. If you wish to get timeout notifications when a bid request from PBS times out, you can implement the +`MakeTimeoutNotification` method in your adapter. If you do not wish timeout notification, do not implement the method. + +`func (a *Adapter) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error)` + +Here the `RequestData` supplied as an argument is the request returned from `MakeRequests` that timed out. If an adapter generates +multiple requests, and more than one of them times out, then there will be a call to `MakeTimeoutNotification` for each failed +request. The function should then return a `RequestData` object that will be the timeout notification to be sent to the bidder, or a list of errors encountered trying to create the timeout notification request. Timeout notifications will not generate subsequent timeout notifications if they timeout or fail. + ## Test Your Bidder ### Automated Tests diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 390016117fb..c8fbb775a21 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -5,6 +5,8 @@ import ( "net/http" "strings" + "github.com/prebid/prebid-server/pbsmetrics" + "github.com/prebid/prebid-server/adapters" ttx "github.com/prebid/prebid-server/adapters/33across" "github.com/prebid/prebid-server/adapters/adform" @@ -85,7 +87,7 @@ import ( // The newAdapterMap function is segregated to its own file to make it a simple and clean location for each Adapter // to register itself. No wading through Exchange code to find it. -func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos) map[openrtb_ext.BidderName]adaptedBidder { +func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos, me pbsmetrics.MetricsEngine) map[openrtb_ext.BidderName]adaptedBidder { ortbBidders := map[openrtb_ext.BidderName]adapters.Bidder{ openrtb_ext.Bidder33Across: ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint), openrtb_ext.BidderAdform: adform.NewAdformBidder(client, cfg.Adapters[string(openrtb_ext.BidderAdform)].Endpoint), @@ -190,7 +192,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter for name, bidder := range ortbBidders { // Clean out any disabled bidders if infos[string(name)].Status == adapters.StatusActive { - allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client) + allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client, cfg, me) } } diff --git a/exchange/adapter_map_test.go b/exchange/adapter_map_test.go index a732f357897..f472ab1d988 100644 --- a/exchange/adapter_map_test.go +++ b/exchange/adapter_map_test.go @@ -7,11 +7,12 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" + metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config" ) func TestNewAdapterMap(t *testing.T) { cfg := &config.Configuration{Adapters: blankAdapterConfig(openrtb_ext.BidderList())} - adapterMap := newAdapterMap(nil, cfg, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList())) + adapterMap := newAdapterMap(nil, cfg, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), &metricsConfig.DummyMetricsEngine{}) for _, bidderName := range openrtb_ext.BidderMap { if bidder, ok := adapterMap[bidderName]; bidder == nil || !ok { t.Errorf("adapterMap missing expected Bidder: %s", string(bidderName)) @@ -38,7 +39,7 @@ func TestNewAdapterMapDisabledAdapters(t *testing.T) { } } } - adapterMap := newAdapterMap(nil, &config.Configuration{Adapters: cfgAdapters}, adapters.ParseBidderInfos(cfgAdapters, "../static/bidder-info", bidderList)) + adapterMap := newAdapterMap(nil, &config.Configuration{Adapters: cfgAdapters}, adapters.ParseBidderInfos(cfgAdapters, "../static/bidder-info", bidderList), &metricsConfig.DummyMetricsEngine{}) for _, bidderName := range openrtb_ext.BidderMap { if bidder, ok := adapterMap[bidderName]; bidder == nil || !ok { if inList(bidderList, bidderName) { diff --git a/exchange/bidder.go b/exchange/bidder.go index 7a53db5ee97..f9b4a522343 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -10,13 +10,18 @@ import ( "net/http" "time" + "github.com/golang/glog" + "github.com/prebid/prebid-server/config/util" + "github.com/mxmCherry/openrtb" nativeRequests "github.com/mxmCherry/openrtb/native/request" nativeResponse "github.com/mxmCherry/openrtb/native/response" "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/pbsmetrics" "golang.org/x/net/context/ctxhttp" ) @@ -82,16 +87,20 @@ type pbsOrtbSeatBid struct { // // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter" // (which is being phased out and replaced by Bidder for OpenRTB auctions) -func adaptBidder(bidder adapters.Bidder, client *http.Client) adaptedBidder { +func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me pbsmetrics.MetricsEngine) adaptedBidder { return &bidderAdapter{ - Bidder: bidder, - Client: client, + Bidder: bidder, + Client: client, + DebugConfig: cfg.Debug, + me: me, } } type bidderAdapter struct { - Bidder adapters.Bidder - Client *http.Client + Bidder adapters.Bidder + Client *http.Client + DebugConfig config.Debug + me pbsmetrics.MetricsEngine } func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currencies.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { @@ -365,8 +374,21 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou httpReq, err := http.NewRequest(toReq.Method, toReq.Uri, bytes.NewBuffer(toReq.Body)) if err == nil { httpReq.Header = req.Headers - ctxhttp.Do(ctx, bidder.Client, httpReq) - // No validation yet on sending notifications + httpResp, err := ctxhttp.Do(ctx, bidder.Client, httpReq) + success := (err == nil && httpResp.StatusCode >= 200 && httpResp.StatusCode < 300) + bidder.me.RecordTimeoutNotice(success) + if bidder.DebugConfig.TimeoutNotification.Log && !(bidder.DebugConfig.TimeoutNotification.FailOnly && success) { + var msg string + if err == nil { + msg = fmt.Sprintf("TimeoutNotification: status:(%d) body:%s", httpResp.StatusCode, string(toReq.Body)) + } else { + msg = fmt.Sprintf("TimeoutNotification: error:(%s) body:%s", err.Error(), string(toReq.Body)) + } + // If logging is turned on, and logging is not disallowed via FailOnly + util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) + } + } else { + bidder.me.RecordTimeoutNotice(false) } } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index f20b431c13a..fa04e6a4771 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -12,8 +12,10 @@ import ( "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/openrtb_ext" + metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config" "github.com/stretchr/testify/assert" nativeRequests "github.com/mxmCherry/openrtb/native/request" @@ -64,7 +66,7 @@ func TestSingleBidder(t *testing.T) { }, bidResponse: mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) @@ -152,7 +154,7 @@ func TestMultiBidder(t *testing.T) { }}, bidResponse: mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) @@ -510,7 +512,7 @@ func TestMultiCurrencies(t *testing.T) { ) // Execute: - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -658,7 +660,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid( context.Background(), @@ -824,7 +826,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -939,7 +941,7 @@ func TestServerCallDebugging(t *testing.T) { Headers: http.Header{}, }, } - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() bids, _ := bidder.requestBid( @@ -1051,7 +1053,7 @@ func TestMobileNativeTypes(t *testing.T) { }, bidResponse: tc.mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client()) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() seatBids, _ := bidder.requestBid( @@ -1072,7 +1074,7 @@ func TestMobileNativeTypes(t *testing.T) { } func TestErrorReporting(t *testing.T) { - bidder := adaptBidder(&bidRejector{}, nil) + bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) currencyConverter := currencies.NewRateConverterDefault() bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if bids != nil { diff --git a/exchange/exchange.go b/exchange/exchange.go index 6d51b87de4a..660beb641ef 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -69,7 +69,7 @@ type bidResponseWrapper struct { func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currencies.RateConverter) Exchange { e := new(exchange) - e.adapterMap = newAdapterMap(client, cfg, infos) + e.adapterMap = newAdapterMap(client, cfg, infos, metricsEngine) e.cache = cache e.cacheTime = time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond e.me = metricsEngine diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index f86309684c6..72de1d4261f 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -8,12 +8,14 @@ import ( "testing" "time" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/gdpr" "github.com/prebid/prebid-server/pbsmetrics" metricsConf "github.com/prebid/prebid-server/pbsmetrics/config" + metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" @@ -132,7 +134,7 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb.Bid, mockServerU adapterMap[bidder] = adaptBidder(&mockTargetingBidder{ mockServerURL: mockServerURL, bids: bids, - }, client) + }, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) } return adapterMap } diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go index e1cdaceb0e5..4e249785ba6 100644 --- a/pbsmetrics/config/metrics.go +++ b/pbsmetrics/config/metrics.go @@ -188,6 +188,13 @@ func (me *MultiMetricsEngine) RecordRequestQueueTime(success bool, requestType p } } +// RecordTimeoutNotice across all engines +func (me *MultiMetricsEngine) RecordTimeoutNotice(success bool) { + for _, thisME := range *me { + thisME.RecordTimeoutNotice(success) + } +} + // DummyMetricsEngine is a Noop metrics engine in case no metrics are configured. (may also be useful for tests) type DummyMetricsEngine struct{} @@ -262,3 +269,7 @@ func (me *DummyMetricsEngine) RecordPrebidCacheRequestTime(success bool, length // RecordRequestQueueTime as a noop func (me *DummyMetricsEngine) RecordRequestQueueTime(success bool, requestType pbsmetrics.RequestType, length time.Duration) { } + +// RecordTimeoutNotice as a noop +func (me *DummyMetricsEngine) RecordTimeoutNotice(success bool) { +} diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go index ff3d9681fb1..1ced4d57269 100644 --- a/pbsmetrics/go_metrics.go +++ b/pbsmetrics/go_metrics.go @@ -48,6 +48,9 @@ type Metrics struct { ImpsTypeAudio metrics.Meter ImpsTypeNative metrics.Meter + TimeoutNotificationSuccess metrics.Meter + TimeoutNotificationFailure metrics.Meter + AdapterMetrics map[openrtb_ext.BidderName]*AdapterMetrics // Don't export accountMetrics because we need helper functions here to insure its properly populated dynamically accountMetrics map[string]*accountMetrics @@ -131,6 +134,9 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa ImpsTypeAudio: blankMeter, ImpsTypeNative: blankMeter, + TimeoutNotificationSuccess: blankMeter, + TimeoutNotificationFailure: blankMeter, + AdapterMetrics: make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)), accountMetrics: make(map[string]*accountMetrics), MetricsDisabled: disableMetrics, @@ -209,6 +215,9 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d newMetrics.userSyncSet[unknownBidder] = metrics.GetOrRegisterMeter("usersync.unknown.sets", registry) newMetrics.userSyncGDPRPrevent[unknownBidder] = metrics.GetOrRegisterMeter("usersync.unknown.gdpr_prevent", registry) + + newMetrics.TimeoutNotificationSuccess = metrics.GetOrRegisterMeter("timeout_notification.ok", registry) + newMetrics.TimeoutNotificationFailure = metrics.GetOrRegisterMeter("timeout_notification.failed", registry) return newMetrics } @@ -544,6 +553,15 @@ func (me *Metrics) RecordRequestQueueTime(success bool, requestType RequestType, } +func (me *Metrics) RecordTimeoutNotice(success bool) { + if success { + me.TimeoutNotificationSuccess.Mark(1) + } else { + me.TimeoutNotificationFailure.Mark(1) + } + return +} + func doMark(bidder openrtb_ext.BidderName, meters map[openrtb_ext.BidderName]metrics.Meter) { met, ok := meters[bidder] if ok { diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go index 253ff69e3c2..25f75e77758 100644 --- a/pbsmetrics/go_metrics_test.go +++ b/pbsmetrics/go_metrics_test.go @@ -53,6 +53,9 @@ func TestNewMetrics(t *testing.T) { ensureContains(t, registry, "queued_requests.video.rejected", m.RequestsQueueTimer[ReqTypeVideo][false]) ensureContains(t, registry, "queued_requests.video.accepted", m.RequestsQueueTimer[ReqTypeVideo][true]) + + ensureContains(t, registry, "timeout_notification.ok", m.TimeoutNotificationSuccess) + ensureContains(t, registry, "timeout_notification.failed", m.TimeoutNotificationFailure) } func TestRecordBidType(t *testing.T) { diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index 611692c9c01..e65ba313338 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -275,4 +275,5 @@ type MetricsEngine interface { RecordStoredImpCacheResult(cacheResult CacheResult, inc int) RecordPrebidCacheRequestTime(success bool, length time.Duration) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) + RecordTimeoutNotice(sucess bool) } diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go index 1f5b84b1e0f..482cbf24fae 100644 --- a/pbsmetrics/metrics_mock.go +++ b/pbsmetrics/metrics_mock.go @@ -101,3 +101,8 @@ func (me *MetricsEngineMock) RecordPrebidCacheRequestTime(success bool, length t func (me *MetricsEngineMock) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) { me.Called(success, requestType, length) } + +// RecordTimeoutNotice mock +func (me *MetricsEngineMock) RecordTimeoutNotice(success bool) { + me.Called(success) +} diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index d66defea4cd..e385b044981 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -28,6 +28,7 @@ type Metrics struct { requestsWithoutCookie *prometheus.CounterVec storedImpressionsCacheResult *prometheus.CounterVec storedRequestCacheResult *prometheus.CounterVec + timeout_notifications *prometheus.CounterVec // Adapter Metrics adapterBids *prometheus.CounterVec @@ -79,6 +80,11 @@ const ( requestRejectLabel = "requestRejectedLabel" ) +const ( + requestSuccessful = "ok" + requestFailed = "failed" +) + // NewMetrics initializes a new Prometheus metrics instance with preloaded label values. func NewMetrics(cfg config.PrometheusMetrics) *Metrics { requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} @@ -147,6 +153,11 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of stored request cache requests attempts by hits or miss.", []string{cacheResultLabel}) + metrics.timeout_notifications = newCounter(cfg, metrics.Registry, + "timeout_notification", + "Count of timeout notifications triggered, and if they were successfully sent.", + []string{successLabel}) + metrics.adapterBids = newCounter(cfg, metrics.Registry, "adapter_bids", "Count of bids labeled by adapter and markup delivery type (adm or nurl).", @@ -398,3 +409,15 @@ func (m *Metrics) RecordRequestQueueTime(success bool, requestType pbsmetrics.Re requestStatusLabel: successLabelFormatted, }).Observe(length.Seconds()) } + +func (m *Metrics) RecordTimeoutNotice(success bool) { + if success { + m.timeout_notifications.With(prometheus.Labels{ + successLabel: requestSuccessful, + }).Inc() + } else { + m.timeout_notifications.With(prometheus.Labels{ + successLabel: requestFailed, + }).Inc() + } +} diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index e4d6a4f78d1..24c50492139 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -923,6 +923,27 @@ func TestRecordRequestQueueTimeMetric(t *testing.T) { } } +func TestTimeoutNotifications(t *testing.T) { + m := createMetricsForTesting() + + m.RecordTimeoutNotice(true) + m.RecordTimeoutNotice(true) + m.RecordTimeoutNotice(false) + + assertCounterVecValue(t, "", "timeout_notifications:ok", m.timeout_notifications, + float64(2), + prometheus.Labels{ + successLabel: requestSuccessful, + }) + + assertCounterVecValue(t, "", "timeout_notifications:fail", m.timeout_notifications, + float64(1), + prometheus.Labels{ + successLabel: requestFailed, + }) + +} + func assertCounterValue(t *testing.T, description, name string, counter prometheus.Counter, expected float64) { m := dto.Metric{} counter.Write(&m) From 4361bf64f83a085227ac97781b43f0f30e60a053 Mon Sep 17 00:00:00 2001 From: Gena Date: Tue, 9 Jun 2020 19:32:38 +0300 Subject: [PATCH 110/318] Add Adtarget server adapter (#1319) * Add Adtarget server adapter * Suggested changes for Adtarget --- adapters/adtarget/adtarget.go | 189 ++++++++++++++++++ adapters/adtarget/adtarget_test.go | 11 + .../exemplary/media-type-mapping.json | 88 ++++++++ .../adtargettest/exemplary/simple-banner.json | 62 ++++++ .../adtargettest/exemplary/simple-video.json | 55 +++++ .../adtargettest/params/race/banner.json | 3 + .../adtargettest/params/race/video.json | 3 + .../adtargettest/supplemental/audio.json | 25 +++ .../supplemental/explicit-dimensions.json | 58 ++++++ .../adtargettest/supplemental/native.json | 25 +++ .../supplemental/wrong-impression-ext.json | 26 +++ .../wrong-impression-mapping.json | 77 +++++++ adapters/adtarget/params_test.go | 60 ++++++ adapters/adtarget/usersync.go | 12 ++ adapters/adtarget/usersync_test.go | 37 ++++ config/config.go | 2 + docs/bidders/adtarget.md | 5 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adtarget.go | 9 + static/bidder-info/adtarget.yaml | 11 + static/bidder-params/adtarget.json | 26 +++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 24 files changed, 791 insertions(+) create mode 100644 adapters/adtarget/adtarget.go create mode 100644 adapters/adtarget/adtarget_test.go create mode 100644 adapters/adtarget/adtargettest/exemplary/media-type-mapping.json create mode 100644 adapters/adtarget/adtargettest/exemplary/simple-banner.json create mode 100644 adapters/adtarget/adtargettest/exemplary/simple-video.json create mode 100644 adapters/adtarget/adtargettest/params/race/banner.json create mode 100644 adapters/adtarget/adtargettest/params/race/video.json create mode 100644 adapters/adtarget/adtargettest/supplemental/audio.json create mode 100644 adapters/adtarget/adtargettest/supplemental/explicit-dimensions.json create mode 100644 adapters/adtarget/adtargettest/supplemental/native.json create mode 100644 adapters/adtarget/adtargettest/supplemental/wrong-impression-ext.json create mode 100644 adapters/adtarget/adtargettest/supplemental/wrong-impression-mapping.json create mode 100644 adapters/adtarget/params_test.go create mode 100644 adapters/adtarget/usersync.go create mode 100644 adapters/adtarget/usersync_test.go create mode 100644 docs/bidders/adtarget.md create mode 100644 openrtb_ext/imp_adtarget.go create mode 100644 static/bidder-info/adtarget.yaml create mode 100644 static/bidder-params/adtarget.json diff --git a/adapters/adtarget/adtarget.go b/adapters/adtarget/adtarget.go new file mode 100644 index 00000000000..77622d458a4 --- /dev/null +++ b/adapters/adtarget/adtarget.go @@ -0,0 +1,189 @@ +package adtarget + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type AdtargetAdapter struct { + endpoint string +} + +type adtargetImpExt struct { + Adtarget openrtb_ext.ExtImpAdtarget `json:"adtarget"` +} + +func (a *AdtargetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + totalImps := len(request.Imp) + errors := make([]error, 0, totalImps) + imp2source := make(map[int][]int) + + for i := 0; i < totalImps; i++ { + + sourceId, err := validateImpressionAndSetExt(&request.Imp[i]) + + if err != nil { + errors = append(errors, err) + continue + } + + if _, ok := imp2source[sourceId]; !ok { + imp2source[sourceId] = make([]int, 0, totalImps-i) + } + + imp2source[sourceId] = append(imp2source[sourceId], i) + + } + + totalReqs := len(imp2source) + if 0 == totalReqs { + return nil, errors + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + reqs := make([]*adapters.RequestData, 0, totalReqs) + + imps := request.Imp + request.Imp = make([]openrtb.Imp, 0, len(imps)) + for sourceId, impIndexes := range imp2source { + request.Imp = request.Imp[:0] + + for i := 0; i < len(impIndexes); i++ { + request.Imp = append(request.Imp, imps[impIndexes[i]]) + } + + body, err := json.Marshal(request) + if err != nil { + errors = append(errors, fmt.Errorf("error while encoding bidRequest, err: %s", err)) + return nil, errors + } + + reqs = append(reqs, &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint + fmt.Sprintf("?aid=%d", sourceId), + Body: body, + Headers: headers, + }) + } + + return reqs, errors +} + +func (a *AdtargetAdapter) MakeBids(bidReq *openrtb.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + if httpRes.StatusCode == http.StatusNoContent { + return nil, nil + } + if httpRes.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode), + }} + } + var bidResp openrtb.BidResponse + if err := json.Unmarshal(httpRes.Body, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("error while decoding response, err: %s", err), + }} + } + + bidResponse := adapters.NewBidderResponse() + var errors []error + + var impOK bool + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + + bid := sb.Bid[i] + + impOK = false + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range bidReq.Imp { + if imp.ID == bid.ImpID { + + impOK = true + + if imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + break + } + } + } + + if !impOK { + errors = append(errors, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("ignoring bid id=%s, request doesn't contain any impression with id=%s", bid.ID, bid.ImpID), + }) + continue + } + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: mediaType, + }) + } + } + + return bidResponse, errors +} + +func validateImpressionAndSetExt(imp *openrtb.Imp) (int, error) { + + if imp.Banner == nil && imp.Video == nil { + return 0, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, Adtarget supports only Video and Banner", imp.ID), + } + } + + if 0 == len(imp.Ext) { + return 0, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, extImpBidder is empty", imp.ID), + } + } + + var bidderExt adapters.ExtImpBidder + + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return 0, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err), + } + } + + impExt := openrtb_ext.ExtImpAdtarget{} + err := json.Unmarshal(bidderExt.Bidder, &impExt) + if err != nil { + return 0, &errortypes.BadInput{ + Message: fmt.Sprintf("ignoring imp id=%s, error while decoding impExt, err: %s", imp.ID, err), + } + } + + // common extension for all impressions + var impExtBuffer []byte + + impExtBuffer, err = json.Marshal(&adtargetImpExt{ + Adtarget: impExt, + }) + + if impExt.BidFloor > 0 { + imp.BidFloor = impExt.BidFloor + } + + imp.Ext = impExtBuffer + + return impExt.SourceId, nil +} + +func NewAdtargetBidder(endpoint string) *AdtargetAdapter { + return &AdtargetAdapter{ + endpoint: endpoint, + } +} diff --git a/adapters/adtarget/adtarget_test.go b/adapters/adtarget/adtarget_test.go new file mode 100644 index 00000000000..93732988120 --- /dev/null +++ b/adapters/adtarget/adtarget_test.go @@ -0,0 +1,11 @@ +package adtarget + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "adtargettest", NewAdtargetBidder("http://ghb.console.adtarget.com.tr/pbs/ortb")) +} diff --git a/adapters/adtarget/adtargettest/exemplary/media-type-mapping.json b/adapters/adtarget/adtargettest/exemplary/media-type-mapping.json new file mode 100644 index 00000000000..518268d4fea --- /dev/null +++ b/adapters/adtarget/adtargettest/exemplary/media-type-mapping.json @@ -0,0 +1,88 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "aid": 1000 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ghb.console.adtarget.com.tr/pbs/ortb?aid=1000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "adtarget": { + "aid": 1000 + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 3.5, + "w": 900, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 3.5, + "w": 900, + "h": 250 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adtarget/adtargettest/exemplary/simple-banner.json b/adapters/adtarget/adtargettest/exemplary/simple-banner.json new file mode 100644 index 00000000000..b63739bda0f --- /dev/null +++ b/adapters/adtarget/adtargettest/exemplary/simple-banner.json @@ -0,0 +1,62 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "aid": 1000, + "siteId": 1234, + "bidFloor": 20 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ghb.console.adtarget.com.tr/pbs/ortb?aid=1000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [ + {"w":300,"h":250}, + {"w":300,"h":600} + ] + }, + "bidfloor": 20, + "ext": { + "adtarget": { + "aid": 1000, + "siteId": 1234, + "bidFloor": 20 + } + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} \ No newline at end of file diff --git a/adapters/adtarget/adtargettest/exemplary/simple-video.json b/adapters/adtarget/adtargettest/exemplary/simple-video.json new file mode 100644 index 00000000000..4dc4547d7d1 --- /dev/null +++ b/adapters/adtarget/adtargettest/exemplary/simple-video.json @@ -0,0 +1,55 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "aid": 1000 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ghb.console.adtarget.com.tr/pbs/ortb?aid=1000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "adtarget": { + "aid": 1000 + } + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} \ No newline at end of file diff --git a/adapters/adtarget/adtargettest/params/race/banner.json b/adapters/adtarget/adtargettest/params/race/banner.json new file mode 100644 index 00000000000..1d6658c71ab --- /dev/null +++ b/adapters/adtarget/adtargettest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "aid": 350975 +} diff --git a/adapters/adtarget/adtargettest/params/race/video.json b/adapters/adtarget/adtargettest/params/race/video.json new file mode 100644 index 00000000000..fe4207ef05c --- /dev/null +++ b/adapters/adtarget/adtargettest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "aid": 331133 +} diff --git a/adapters/adtarget/adtargettest/supplemental/audio.json b/adapters/adtarget/adtargettest/supplemental/audio.json new file mode 100644 index 00000000000..e2148e9db99 --- /dev/null +++ b/adapters/adtarget/adtargettest/supplemental/audio.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "unsupported-audio-request", + "imp": [ + { + "id": "unsupported-audio-imp", + "audio": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "ignoring imp id=unsupported-audio-imp, Adtarget supports only Video and Banner", + "comparison": "literal" + } + ] +} diff --git a/adapters/adtarget/adtargettest/supplemental/explicit-dimensions.json b/adapters/adtarget/adtargettest/supplemental/explicit-dimensions.json new file mode 100644 index 00000000000..a4e487466ea --- /dev/null +++ b/adapters/adtarget/adtargettest/supplemental/explicit-dimensions.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "bidder": { + "aid": 1000 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ghb.console.adtarget.com.tr/pbs/ortb?aid=1000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 100, + "h": 400 + }, + "ext": { + "adtarget": { + "aid": 1000 + } + } + } + ] + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/adtarget/adtargettest/supplemental/native.json b/adapters/adtarget/adtargettest/supplemental/native.json new file mode 100644 index 00000000000..3d9aa6630eb --- /dev/null +++ b/adapters/adtarget/adtargettest/supplemental/native.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "unsupported-native-request", + "imp": [ + { + "id": "unsupported-native-imp", + "native": { + "ver": "1.1" + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "ignoring imp id=unsupported-native-imp, Adtarget supports only Video and Banner", + "comparison": "literal" + } + ] +} diff --git a/adapters/adtarget/adtargettest/supplemental/wrong-impression-ext.json b/adapters/adtarget/adtargettest/supplemental/wrong-impression-ext.json new file mode 100644 index 00000000000..1986dfaf13f --- /dev/null +++ b/adapters/adtarget/adtargettest/supplemental/wrong-impression-ext.json @@ -0,0 +1,26 @@ +{ + "mockBidRequest": { + "id": "unsupported-native-request", + "imp": [ + { + "id": "unsupported-native-imp", + "video": { + "w": 100, + "h": 200 + }, + "ext": { + "bidder": { + "aid": "some string instead of int" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "ignoring imp id=unsupported-native-imp, error while decoding impExt, err: json: cannot unmarshal string into Go struct field ExtImpAdtarget.aid of type int", + "comparison": "literal" + } + ] +} diff --git a/adapters/adtarget/adtargettest/supplemental/wrong-impression-mapping.json b/adapters/adtarget/adtargettest/supplemental/wrong-impression-mapping.json new file mode 100644 index 00000000000..0dffdb2bebb --- /dev/null +++ b/adapters/adtarget/adtargettest/supplemental/wrong-impression-mapping.json @@ -0,0 +1,77 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "aid": 1000 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ghb.console.adtarget.com.tr/pbs/ortb?aid=1000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 900, + "h": 250, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "adtarget": { + "aid": 1000 + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "SOME-WRONG-IMP-ID", + "price": 3.5, + "w": 900, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "ignoring bid id=test-bid-id, request doesn't contain any impression with id=SOME-WRONG-IMP-ID", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/adtarget/params_test.go b/adapters/adtarget/params_test.go new file mode 100644 index 00000000000..b128d11c9cf --- /dev/null +++ b/adapters/adtarget/params_test.go @@ -0,0 +1,60 @@ +package adtarget + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/adtarget.json +// These also validate the format of the external API: request.imp[i].ext.adtarget +// TestValidParams makes sure that the adtarget schema accepts all imp.ext fields which we intend to support. + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdtarget, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adtarget params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the adtarget schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdtarget, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"aid":123}`, + `{"aid":123,"placementId":1234}`, + `{"aid":123,"siteId":4321}`, + `{"aid":123,"siteId":0,"bidFloor":0}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"aid":"123"}`, + `{"aid":"0"}`, + `{"aid":"123","placementId":"123"}`, + `{"aid":123, "placementId":"123", "siteId":"321"}`, +} diff --git a/adapters/adtarget/usersync.go b/adapters/adtarget/usersync.go new file mode 100644 index 00000000000..20bced25c72 --- /dev/null +++ b/adapters/adtarget/usersync.go @@ -0,0 +1,12 @@ +package adtarget + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewAdtargetSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("adtarget", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/adtarget/usersync_test.go b/adapters/adtarget/usersync_test.go new file mode 100644 index 00000000000..3ab2ed5b5df --- /dev/null +++ b/adapters/adtarget/usersync_test.go @@ -0,0 +1,37 @@ +package adtarget + +import ( + "fmt" + "github.com/prebid/prebid-server/privacy/ccpa" + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestAdtargetSyncer(t *testing.T) { + syncURL := "//sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=localhost%2Fsetuid%3Fbidder%3Dadtarget%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D" + fmt.Println("adtarget sync") + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAdtargetSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + Consent: "123", + }, + CCPA: ccpa.Policy{ + Value: "1-YY", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "//sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr=0&gdpr_consent=123&us_privacy=1-YY&redir=localhost%2Fsetuid%3Fbidder%3Dadtarget%26gdpr%3D0%26gdpr_consent%3D123%26uid%3D%7Buid%7D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 559fc5dac19..07384f9d2d3 100755 --- a/config/config.go +++ b/config/config.go @@ -548,6 +548,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernel, "https://sync.adkernel.com/user-sync?t=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadkernel%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdkernelAdn, "https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3DadkernelAdn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdpone, "https://usersync.adpone.com/csync?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadpone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtarget, "https://sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtarget%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") // openrtb_ext.BidderAdOcean doesn't have a good default. @@ -752,6 +753,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") + v.SetDefault("adapters.adtarget.endpoint", "http://ghb.console.adtarget.com.tr/pbs/ortb") v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb") v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.aja.endpoint", "https://ad.as.amanad.adtdp.com/v1/bid/4") diff --git a/docs/bidders/adtarget.md b/docs/bidders/adtarget.md new file mode 100644 index 00000000000..b658a728a2b --- /dev/null +++ b/docs/bidders/adtarget.md @@ -0,0 +1,5 @@ +# Adtarget bidder + +To use the Adtarget bidder you will need an aid from an exchange account on [https://console.adtarget.com.tr](adtarget.com.tr). + +For further information, please contact kamil@adtarget.com.tr \ No newline at end of file diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index c8fbb775a21..2ea8f7fb648 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adoppler" "github.com/prebid/prebid-server/adapters/adpone" + "github.com/prebid/prebid-server/adapters/adtarget" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" "github.com/prebid/prebid-server/adapters/aja" @@ -99,6 +100,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdOcean: adocean.NewAdOceanBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdOcean))].Endpoint), openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), openrtb_ext.BidderAdpone: adpone.NewAdponeBidder(cfg.Adapters[string(openrtb_ext.BidderAdpone)].Endpoint), + openrtb_ext.BidderAdtarget: adtarget.NewAdtargetBidder(cfg.Adapters[string(openrtb_ext.BidderAdtarget)].Endpoint), openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), openrtb_ext.BidderAdvangelists: advangelists.NewAdvangelistsBidder(cfg.Adapters[string(openrtb_ext.BidderAdvangelists)].Endpoint), openrtb_ext.BidderAJA: aja.NewAJABidder(cfg.Adapters[string(openrtb_ext.BidderAJA)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index b3ecddb06cd..659c6616fea 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -33,6 +33,7 @@ const ( BidderAdpone BidderName = "adpone" BidderAdmixer BidderName = "admixer" BidderAdOcean BidderName = "adocean" + BidderAdtarget BidderName = "adtarget" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" BidderAJA BidderName = "aja" @@ -110,6 +111,7 @@ var BidderMap = map[string]BidderName{ "admixer": BidderAdmixer, "adocean": BidderAdOcean, "adpone": BidderAdpone, + "adtarget": BidderAdtarget, "adtelligent": BidderAdtelligent, "advangelists": BidderAdvangelists, "aja": BidderAJA, diff --git a/openrtb_ext/imp_adtarget.go b/openrtb_ext/imp_adtarget.go new file mode 100644 index 00000000000..a8ac70a17d1 --- /dev/null +++ b/openrtb_ext/imp_adtarget.go @@ -0,0 +1,9 @@ +package openrtb_ext + +// ExtImpAdtarget defines the contract for bidrequest.imp[i].ext.adtarget +type ExtImpAdtarget struct { + SourceId int `json:"aid"` + PlacementId int `json:"placementId,omitempty"` + SiteId int `json:"siteId,omitempty"` + BidFloor float64 `json:"bidFloor,omitempty"` +} diff --git a/static/bidder-info/adtarget.yaml b/static/bidder-info/adtarget.yaml new file mode 100644 index 00000000000..d52f18ac697 --- /dev/null +++ b/static/bidder-info/adtarget.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "kamil@adtarget.com.tr" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/adtarget.json b/static/bidder-params/adtarget.json new file mode 100644 index 00000000000..195bf2dd430 --- /dev/null +++ b/static/bidder-params/adtarget.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adtarget Adapter Params", + "description": "A schema which validates params accepted by the Adtarget adapter", + + "type": "object", + "properties": { + "placementId": { + "type": "integer", + "description": "An ID which identifies this placement of the impression" + }, + "siteId": { + "type": "integer", + "description": "An ID which identifies the site selling the impression" + }, + "aid": { + "type": "integer", + "description": "An ID which identifies the channel" + }, + "bidFloor": { + "type": "number", + "description": "BidFloor, US Dollars" + } + }, + "required": ["aid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 5dccf855add..751d2aabfbe 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -12,6 +12,7 @@ import ( "github.com/prebid/prebid-server/adapters/admixer" "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adpone" + "github.com/prebid/prebid-server/adapters/adtarget" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" "github.com/prebid/prebid-server/adapters/aja" @@ -84,6 +85,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdmixer, admixer.NewAdmixerSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdOcean, adocean.NewAdOceanSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdpone, adpone.NewadponeSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAdtarget, adtarget.NewAdtargetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdtelligent, adtelligent.NewAdtelligentSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAJA, aja.NewAJASyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index ddd067e8be7..c9ef382fc92 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -21,6 +21,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdmixer): syncConfig, string(openrtb_ext.BidderAdOcean): syncConfig, string(openrtb_ext.BidderAdpone): syncConfig, + string(openrtb_ext.BidderAdtarget): syncConfig, string(openrtb_ext.BidderAdtelligent): syncConfig, string(openrtb_ext.BidderAdvangelists): syncConfig, string(openrtb_ext.BidderAJA): syncConfig, From 86fa52b19e92348d070d97fefd83d21974bbf25d Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Tue, 9 Jun 2020 13:23:57 -0400 Subject: [PATCH 111/318] Update Auction OpenRTB Sample (#1342) * Update Auction OpenRTB Sample * Removed Extra "Or" --- docs/endpoints/openrtb2/auction.md | 209 +++++++++++++++++------------ 1 file changed, 126 insertions(+), 83 deletions(-) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index 67430e51481..d09216188b8 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -14,53 +14,94 @@ This endpoint runs an auction with the given OpenRTB 2.5 bid request. ### Sample request -The [Prebid sample ad](http://prebid.org/examples/pbjs_demo.html) can be loaded with the request sample [here](../../../endpoints/openrtb2/sample-requests/valid-whole/exemplary/prebid-test-ad.json). +This is a sample OpenRTB 2.5 bid request for a Xandr (formerly AppNexus) test placement. Please note, the Xandr Ad Server will only +respond with a bid if the "test" field is set to 1. -Other examples can be found in [endpoints/openrtb2/sample-requests/valid-whole/exemplary](../../../endpoints/openrtb2/sample-requests/valid-whole/exemplary). +``` +{ + "id": "some-request-id", + "test": 1, + "site": { + "page": "prebid.org" + }, + "imp": [{ + "id": "some-impression-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + }], + "tmax": 500 +} +``` + +Additional examples can be found in [endpoints/openrtb2/sample-requests/valid-whole](../../../endpoints/openrtb2/sample-requests/valid-whole). ### Sample Response This endpoint will respond with either: -- An OpenRTB 2.5 BidResponse, or -- An HTTP 400 status code if the request is malformed +- An OpenRTB 2.5 bid response, or +- HTTP 400 if the request is malformed, or +- HTTP 503 if the account or app specified in the request is blacklisted -A "hello world" response from the prebid sample ad request is shown below. +This is the corresponding response to the above sample OpenRTB 2.5 bid request, with the `ext.debug` field removed and the `seatbid.bid.adm` field simplified. ``` { "id": "some-request-id", - "seatbid": [ - { - "seat": "appnexus" - "bid": [ - { - "id": "4625436751433509010", - "impid": "some-impression-id", - "price": 0.5, - "adm": "", - "adid": "29681110", - "adomain": [ - "appnexus.com" - ], - "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110", - "cid": "958", - "crid": "29681110", - "w": 300, - "h": 250, - "ext": { - "bidder": { - "appnexus": { - "brand_id": 1, - "auction_id": 6127490747252133000, - "bidder_id": 2 - } - } + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "145556724130495288", + "impid": "some-impression-id", + "price": 0.01, + "adm": "", + "adid": "107987536", + "adomain": [ + "appnexus.com" + ], + "iurl": "https://nym1-ib.adnxs.com/cr?id=107987536", + "cid": "3532", + "crid": "107987536", + "w": 600, + "h": 500, + "ext": { + "prebid": { + "type": "banner", + "video": { + "duration": 0, + "primary_category": "" + } + }, + "bidder": { + "appnexus": { + "brand_id": 1, + "auction_id": 7311907164510136364, + "bidder_id": 2, + "bid_ad_type": 0 } } - ] - } - ] + } + }] + }], + "cur": "USD", + "ext": { + "responsetimemillis": { + "appnexus": 10 + }, + "tmaxrequest": 500 + } } ``` @@ -69,12 +110,12 @@ A "hello world" response from the prebid sample ad request is shown below. #### Conventions OpenRTB 2.5 permits exchanges to define their own extensions to any object from the spec. -These fall under the `ext` property of JSON objects. +These fall under the `ext` field of JSON objects. If `ext` is defined on an object, Prebid Server uses the following conventions: -1. `ext` in "Request objects" uses `ext.prebid` and/or `ext.{anyBidderCode}`. -2. `ext` on "Response objects" uses `ext.prebid` and/or `ext.bidder`. +1. `ext` in "request objects" uses `ext.prebid` and/or `ext.{anyBidderCode}`. +2. `ext` on "response objects" uses `ext.prebid` and/or `ext.bidder`. The only exception here is the top-level `BidResponse`, because it's bidder-independent. `ext.{anyBidderCode}` and `ext.bidder` extensions are defined by bidders. @@ -84,9 +125,9 @@ Exceptions are made for extensions with "standard" recommendations: - `request.user.ext.digitrust` -- To support Digitrust - `request.regs.ext.gdpr` and `request.user.ext.consent` -- To support GDPR +- `request.regs.us_privacy` -- To support CCPA - `request.site.ext.amp` -- To identify AMP as the request source - `request.app.ext.source` and `request.app.ext.version` -- To support identifying the displaymanager/SDK in mobile apps. If given, we expect these to be strings. -- `request.regs.coppa` -- to support COPPA #### Bid Adjustments @@ -98,7 +139,7 @@ If you find that some bidders use Gross bids, publishers can adjust for it with "ext": { "prebid": { "bidadjustmentfactors": { - "appnexus: 0.8, + "appnexus": 0.8, "rubicon": 0.7 } } @@ -126,8 +167,8 @@ to set these params on the response at `response.seatbid[i].bid[j].ext.prebid.ta "pricegranularity": { "precision": 2, "ranges": [{ - "max":20.00, - "increment":0.10 // This is equivalent to the deprecated "pricegranularity": "medium" + "max": 20.00, + "increment": 0.10 // This is equivalent to the deprecated "pricegranularity": "medium" }] }, "includewinners": false, // Optional param defaulting to true @@ -146,23 +187,29 @@ One of "includewinners" or "includebidderkeys" must be true (both default to tru MediaType PriceGranularity (PBS-Java only) - when a single OpenRTB request contains multiple impressions with different mediatypes, or a single impression supports multiple formats, the different mediatypes may need different price granularities. If `mediatypepricegranularity` is present, `pricegranularity` would only be used for any mediatypes not specified. ``` - "ext": { - "prebid": { - "targeting": { - "mediatypepricegranularity": { - "banner": { "ranges": [ - {"max": 20, "increment": 0.5} - ]}, - "video": { "ranges": [ - {"max": 10, "increment": 1}, - {"max": 20, "increment": 2}, - {"max": 50, "increment": 5} - ]} - } - } - "includewinners": true - } - } +{ + "ext": { + "prebid": { + "targeting": { + "mediatypepricegranularity": { + "banner": { + "ranges": [ + {"max": 20, "increment": 0.5} + ] + }, + "video": { + "ranges": [ + {"max": 10, "increment": 1}, + {"max": 20, "increment": 2}, + {"max": 50, "increment": 5} + ] + } + } + }, + "includewinners": true + } + } +} ``` **Response format** (returned in `bid.ext.prebid.targeting`) @@ -238,22 +285,20 @@ This can be used to request bids from the same Bidder with different params. For ``` { - "imp": [ - { - "id": "some-impression-id", - "video": { - "mimes": ["video/mp4"] + "imp": [{ + "id": "some-impression-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 123 }, - "ext": { - "appnexus: { - "placementId": 123 - }, - "districtm": { - "placementId": 456 - } + "districtm": { + "placementId": 456 } } - ], + }], "ext": { "prebid": { "aliases": { @@ -303,12 +348,12 @@ For example, a request may return this in `response.ext` "ext": { "errors": { "appnexus": [{ - "code": 2, - "message": "A hybrid Banner/Audio Imp was offered, but Appnexus doesn't support Audio." + "code": 2, + "message": "A hybrid Banner/Audio Imp was offered, but Appnexus doesn't support Audio." }], "rubicon": [{ - "code": 1, - "message": "The request exceeded the timeout allocated" + "code": 1, + "message": "The request exceeded the timeout allocated" }] } } @@ -413,16 +458,14 @@ The values will be numbers that indicate the minimum allowed size for the ad, as Example: ``` { - "imp": [ - { - ... - "banner": { - ... - } - "instl": 1, + "imp": [{ + ... + "banner": { ... } - ] + "instl": 1, + ... + }] "device": { ... "h": 640, From 24665e8341ce985de7b7524e35a63962ffe5146d Mon Sep 17 00:00:00 2001 From: Brandon Ling <51931757+blingster7@users.noreply.github.com> Date: Thu, 11 Jun 2020 14:10:50 -0400 Subject: [PATCH 112/318] Triplelift: Add SRA Support (#1347) --- adapters/triplelift/triplelift_test.go | 2 +- .../triplelifttest/exemplary/optional-params.json | 2 +- .../triplelift/triplelifttest/exemplary/simple-banner.json | 2 +- .../triplelift/triplelifttest/exemplary/simple-video.json | 6 +++--- .../triplelifttest/supplemental/badresponseext.json | 2 +- .../triplelifttest/supplemental/badstatuscode.json | 2 +- .../triplelifttest/supplemental/notgoodstatuscode.json | 2 +- config/config.go | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/adapters/triplelift/triplelift_test.go b/adapters/triplelift/triplelift_test.go index 2d7ed04f51b..6fd2b506f8a 100644 --- a/adapters/triplelift/triplelift_test.go +++ b/adapters/triplelift/triplelift_test.go @@ -6,5 +6,5 @@ import ( ) func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "triplelifttest", NewTripleliftBidder(nil, "http://tlx.3lift.net/s2s/auction?supplier_id=20")) + adapterstest.RunJSONBidderTest(t, "triplelifttest", NewTripleliftBidder(nil, "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20")) } diff --git a/adapters/triplelift/triplelifttest/exemplary/optional-params.json b/adapters/triplelift/triplelifttest/exemplary/optional-params.json index 0851bc096d7..90c8da5b3c1 100644 --- a/adapters/triplelift/triplelifttest/exemplary/optional-params.json +++ b/adapters/triplelift/triplelifttest/exemplary/optional-params.json @@ -28,7 +28,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/triplelift/triplelifttest/exemplary/simple-banner.json b/adapters/triplelift/triplelifttest/exemplary/simple-banner.json index ff680037a7e..156e07e37eb 100644 --- a/adapters/triplelift/triplelifttest/exemplary/simple-banner.json +++ b/adapters/triplelift/triplelifttest/exemplary/simple-banner.json @@ -27,7 +27,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/triplelift/triplelifttest/exemplary/simple-video.json b/adapters/triplelift/triplelifttest/exemplary/simple-video.json index 185446bd243..846c62b4d37 100644 --- a/adapters/triplelift/triplelifttest/exemplary/simple-video.json +++ b/adapters/triplelift/triplelifttest/exemplary/simple-video.json @@ -33,7 +33,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ @@ -85,7 +85,7 @@ "adomain": [ "foo.com" ], - "iurl": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "iurl": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "cid": "958", "crid": "29681110", "h": 250, @@ -122,7 +122,7 @@ "adomain": [ "foo.com" ], - "iurl": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "iurl": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "cid": "958", "crid": "29681110", "w": 300, diff --git a/adapters/triplelift/triplelifttest/supplemental/badresponseext.json b/adapters/triplelift/triplelifttest/supplemental/badresponseext.json index 324c05825c9..6c09448fc4a 100644 --- a/adapters/triplelift/triplelifttest/supplemental/badresponseext.json +++ b/adapters/triplelift/triplelifttest/supplemental/badresponseext.json @@ -27,7 +27,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/triplelift/triplelifttest/supplemental/badstatuscode.json b/adapters/triplelift/triplelifttest/supplemental/badstatuscode.json index 15799616933..f24eb7998ed 100644 --- a/adapters/triplelift/triplelifttest/supplemental/badstatuscode.json +++ b/adapters/triplelift/triplelifttest/supplemental/badstatuscode.json @@ -27,7 +27,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/triplelift/triplelifttest/supplemental/notgoodstatuscode.json b/adapters/triplelift/triplelifttest/supplemental/notgoodstatuscode.json index 963db593776..bdcc0e3a666 100644 --- a/adapters/triplelift/triplelifttest/supplemental/notgoodstatuscode.json +++ b/adapters/triplelift/triplelifttest/supplemental/notgoodstatuscode.json @@ -27,7 +27,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://tlx.3lift.net/s2s/auction?supplier_id=20", + "uri": "http://tlx.3lift.net/s2s/auction?sra=1&supplier_id=20", "body": { "id": "test-request-id", "imp": [ diff --git a/config/config.go b/config/config.go index 07384f9d2d3..56b6a1ba88d 100755 --- a/config/config.go +++ b/config/config.go @@ -805,7 +805,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.telaria.endpoint", "https://ads.tremorhub.com/ad/rtb/prebid") v.SetDefault("adapters.triplelift_native.disabled", true) v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}") - v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20") + v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?sra=1&supplier_id=20") v.SetDefault("adapters.ucfunnel.endpoint", "http://apac-hk-adx.aralego.com/prebid") v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2") v.SetDefault("adapters.valueimpression.endpoint", "https://rtb.valueimpression.com/endpoint") From eb77b170618b581833a1029264a7b39027644e1a Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 15 Jun 2020 10:24:36 -0400 Subject: [PATCH 113/318] Privacy: Limit Ad Tracking (#1334) --- config/config.go | 13 ++ config/config_test.go | 3 + exchange/exchange.go | 10 +- exchange/exchange_test.go | 17 ++- .../exchangetest/lmt-featureflag-off.json | 63 +++++++++ exchange/exchangetest/lmt-featureflag-on.json | 61 +++++++++ exchange/utils.go | 25 +++- exchange/utils_test.go | 94 +++++++++++-- privacy/enforcement.go | 9 +- privacy/enforcement_test.go | 53 ++++++-- privacy/lmt/policy.go | 33 +++++ privacy/lmt/policy_test.go | 128 ++++++++++++++++++ 12 files changed, 473 insertions(+), 36 deletions(-) create mode 100644 exchange/exchangetest/lmt-featureflag-off.json create mode 100644 exchange/exchangetest/lmt-featureflag-on.json create mode 100644 privacy/lmt/policy.go create mode 100644 privacy/lmt/policy_test.go diff --git a/config/config.go b/config/config.go index 56b6a1ba88d..0f470c6a611 100755 --- a/config/config.go +++ b/config/config.go @@ -49,6 +49,7 @@ type Configuration struct { AMPTimeoutAdjustment int64 `mapstructure:"amp_timeout_adjustment_ms"` GDPR GDPR `mapstructure:"gdpr"` CCPA CCPA `mapstructure:"ccpa"` + LMT LMT `mapstructure:"lmt"` CurrencyConverter CurrencyConverter `mapstructure:"currency_converter"` DefReqConfig DefReqConfig `mapstructure:"default_request"` @@ -139,6 +140,13 @@ func (cfg *AuctionTimeouts) LimitAuctionTimeout(requested time.Duration) time.Du return requested } +// Privacy is a grouping of privacy related configs to assist in dependency injection. +type Privacy struct { + CCPA CCPA + GDPR GDPR + LMT LMT +} + type GDPR struct { HostVendorID int `mapstructure:"host_vendor_id"` UsersyncIfAmbiguous bool `mapstructure:"usersync_if_ambiguous"` @@ -193,6 +201,10 @@ type CCPA struct { Enforce bool `mapstructure:"enforce"` } +type LMT struct { + Enforce bool `mapstructure:"enforce"` +} + type Analytics struct { File FileLogs `mapstructure:"file"` } @@ -836,6 +848,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("gdpr.tcf2.purpose_one_treatement.access_allowed", true) v.SetDefault("gdpr.amp_exception", false) v.SetDefault("ccpa.enforce", false) + v.SetDefault("lmt.enforce", true) v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") v.SetDefault("currency_converter.fetch_interval_seconds", 1800) // fetch currency rates every 30 minutes v.SetDefault("default_request.type", "") diff --git a/config/config_test.go b/config/config_test.go index ee8e68e7025..2b291fe978d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -43,6 +43,8 @@ gdpr: non_standard_publishers: ["siteID","fake-site-id","appID","agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA"] ccpa: enforce: true +lmt: + enforce: true host_cookie: cookie_name: userid family: prebid @@ -240,6 +242,7 @@ func TestFullConfig(t *testing.T) { cmpBools(t, "cfg.GDPR.NonStandardPublisherMap", found, false) cmpBools(t, "ccpa.enforce", cfg.CCPA.Enforce, true) + cmpBools(t, "lmt.enforce", cfg.LMT.Enforce, true) //Assert the NonStandardPublishers was correctly unmarshalled cmpStrings(t, "blacklisted_apps", cfg.BlacklistedApps[0], "spamAppID") diff --git a/exchange/exchange.go b/exchange/exchange.go index 660beb641ef..84ae35d644c 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -48,7 +48,7 @@ type exchange struct { currencyConverter *currencies.RateConverter UsersyncIfAmbiguous bool defaultTTLs config.DefaultTTLs - enforceCCPA bool + privacyConfig config.Privacy } // Container to pass out response ext data from the GetAllBids goroutines back into the main thread @@ -77,7 +77,11 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con e.currencyConverter = currencyConverter e.UsersyncIfAmbiguous = cfg.GDPR.UsersyncIfAmbiguous e.defaultTTLs = cfg.CacheURL.DefaultTTLs - e.enforceCCPA = cfg.CCPA.Enforce + e.privacyConfig = config.Privacy{ + CCPA: cfg.CCPA, + GDPR: cfg.GDPR, + LMT: cfg.LMT, + } return e } @@ -100,7 +104,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.enforceCCPA) + cleanRequests, aliases, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) // List of bidders we have requests for. liveAdapters := listBiddersWithRequests(cleanRequests) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index e9b2127e18b..4f329962a53 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -731,7 +731,17 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { if len(errs) != 0 { t.Fatalf("%s: Failed to parse aliases", filename) } - ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, spec.EnforceCCPA) + + privacyConfig := config.Privacy{ + CCPA: config.CCPA{ + Enforce: spec.EnforceCCPA, + }, + LMT: config.LMT{ + Enforce: spec.EnforceLMT, + }, + } + + ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig) biddersInAuction := findBiddersInAuction(t, filename, &spec.IncomingRequest.OrtbRequest) categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") if error != nil { @@ -816,7 +826,7 @@ func extractResponseTimes(t *testing.T, context string, bid *openrtb.BidResponse } } -func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, enforceCCPA bool) Exchange { +func newExchangeForTests(t *testing.T, filename string, expectations map[string]*bidderSpec, aliases map[string]string, privacyConfig config.Privacy) Exchange { adapters := make(map[openrtb_ext.BidderName]adaptedBidder) for _, bidderName := range openrtb_ext.BidderMap { if spec, ok := expectations[string(bidderName)]; ok { @@ -854,7 +864,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] gDPR: gdpr.AlwaysAllow{}, currencyConverter: currencies.NewRateConverterDefault(), UsersyncIfAmbiguous: false, - enforceCCPA: enforceCCPA, + privacyConfig: privacyConfig, } } @@ -1620,6 +1630,7 @@ type exchangeSpec struct { OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` Response exchangeResponse `json:"response,omitempty"` EnforceCCPA bool `json:"enforceCcpa"` + EnforceLMT bool `json:"enforceLmt"` DebugLog *DebugLog `json:"debuglog,omitempty"` } diff --git a/exchange/exchangetest/lmt-featureflag-off.json b/exchange/exchangetest/lmt-featureflag-off.json new file mode 100644 index 00000000000..9a15c87953e --- /dev/null +++ b/exchange/exchangetest/lmt-featureflag-off.json @@ -0,0 +1,63 @@ +{ + "enforceLmt": false, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "device": { + "lmt": 1 + }, + "user": { + "id": "some-id", + "buyeruid": "some-buyer-id" + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "device": { + "lmt": 1 + }, + "user": { + "id": "some-id", + "buyeruid": "some-buyer-id" + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/lmt-featureflag-on.json b/exchange/exchangetest/lmt-featureflag-on.json new file mode 100644 index 00000000000..440f8c76472 --- /dev/null +++ b/exchange/exchangetest/lmt-featureflag-on.json @@ -0,0 +1,61 @@ +{ + "enforceLmt": true, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "device": { + "lmt": 1 + }, + "user": { + "id": "some-id", + "buyeruid": "some-buyer-id" + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "device": { + "lmt": 1 + }, + "user": { + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/utils.go b/exchange/utils.go index f602d1e8fba..54122d13c09 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -8,11 +8,13 @@ import ( "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/gdpr" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/privacy" "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/lmt" ) // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is: @@ -26,8 +28,8 @@ func cleanOpenRTBRequests(ctx context.Context, blables map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels, labels pbsmetrics.Labels, gDPR gdpr.Permissions, - usersyncIfAmbiguous, - enforceCCPA bool) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, errs []error) { + usersyncIfAmbiguous bool, + privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, errs []error) { impsByBidder, errs := splitImps(orig.Imp) if len(errs) > 0 { @@ -45,15 +47,24 @@ func cleanOpenRTBRequests(ctx context.Context, consent := extractConsent(orig) ampGDPRException := (labels.RType == pbsmetrics.ReqTypeAMP) && gDPR.AMPException() - privacyEnforcement := privacy.Enforcement{ - COPPA: orig.Regs != nil && orig.Regs.COPPA == 1, + var ccpaPolicy ccpa.Policy + if privacyConfig.CCPA.Enforce { + ccpaPolicy, _ = ccpa.ReadPolicy(orig) + } + + var lmtPolicy lmt.Policy + if privacyConfig.LMT.Enforce { + lmtPolicy = lmt.ReadPolicy(orig) } - if enforceCCPA { - ccpaPolicy, _ := ccpa.ReadPolicy(orig) - privacyEnforcement.CCPA = ccpaPolicy.ShouldEnforce() + // request level privacy policies + privacyEnforcement := privacy.Enforcement{ + CCPA: ccpaPolicy.ShouldEnforce(), + COPPA: orig.Regs != nil && orig.Regs.COPPA == 1, + LMT: lmtPolicy.ShouldEnforce(), } + // bidder level privacy policies for bidder, bidReq := range requestsByBidder { if gdpr == 1 { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index acbf25ff691..4dad3f54648 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/stretchr/testify/assert" @@ -69,8 +70,17 @@ func TestCleanOpenRTBRequests(t *testing.T) { applyCOPPA: false, consentedVendors: map[string]bool{"appnexus": true, "brightroll": true}}, } + privacyConfig := config.Privacy{ + CCPA: config.CCPA{ + Enforce: true, + }, + LMT: config.LMT{ + Enforce: true, + }, + } + for _, test := range testCases { - reqByBidders, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, true) + reqByBidders, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -99,9 +109,80 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { } for _, test := range testCases { - req := newCCPABidRequest(t) + req := newBidRequest(t) + req.Regs = &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"1-Y-"}`), + } - results, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, test.enforceCCPA) + privacyConfig := config.Privacy{ + CCPA: config.CCPA{ + Enforce: test.enforceCCPA, + }, + } + + results, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + result := results["appnexus"] + + assert.Nil(t, errs) + + if test.expectDataScrub { + assert.Equal(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.Equal(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") + } else { + assert.NotEqual(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.NotEqual(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") + } + } +} + +func TestCleanOpenRTBRequestsLMT(t *testing.T) { + var ( + enabled int8 = 1 + disabled int8 = 0 + ) + testCases := []struct { + description string + lmt *int8 + enforceLMT bool + expectDataScrub bool + }{ + { + description: "Feature Flag Enabled - OpenTRB Enabled", + lmt: &enabled, + enforceLMT: true, + expectDataScrub: true, + }, + { + description: "Feature Flag Disabled - OpenTRB Enabled", + lmt: &enabled, + enforceLMT: false, + expectDataScrub: false, + }, + { + description: "Feature Flag Enabled - OpenTRB Disabled", + lmt: &disabled, + enforceLMT: true, + expectDataScrub: false, + }, + { + description: "Feature Flag Disabled - OpenTRB Disabled", + lmt: &disabled, + enforceLMT: false, + expectDataScrub: false, + }, + } + + for _, test := range testCases { + req := newBidRequest(t) + req.Device.Lmt = test.lmt + + privacyConfig := config.Privacy{ + LMT: config.LMT{ + Enforce: test.enforceLMT, + }, + } + + results, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) @@ -163,8 +244,7 @@ func newAdapterAliasBidRequest(t *testing.T) *openrtb.BidRequest { } } -func newCCPABidRequest(t *testing.T) *openrtb.BidRequest { - dnt := int8(1) +func newBidRequest(t *testing.T) *openrtb.BidRequest { return &openrtb.BidRequest{ Site: &openrtb.Site{ Page: "www.some.domain.com", @@ -178,7 +258,6 @@ func newCCPABidRequest(t *testing.T) *openrtb.BidRequest { UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", IFA: "ifa", IP: "132.173.230.74", - DNT: &dnt, Language: "EN", }, Source: &openrtb.Source{ @@ -189,9 +268,6 @@ func newCCPABidRequest(t *testing.T) *openrtb.BidRequest { BuyerUID: "their-id", Ext: json.RawMessage(`{"digitrust":{"id":"digi-id","keyv":1,"pref":1}}`), }, - Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"1-Y-"}`), - }, Imp: []openrtb.Imp{{ ID: "some-imp-id", Banner: &openrtb.Banner{ diff --git a/privacy/enforcement.go b/privacy/enforcement.go index d302192ec3f..8a5d201fc95 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -10,11 +10,12 @@ type Enforcement struct { COPPA bool GDPR bool GDPRGeo bool + LMT bool } // Any returns true if at least one privacy policy requires enforcement. func (e Enforcement) Any() bool { - return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo + return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo || e.LMT } // Apply cleans personally identifiable information from an OpenRTB bid request. @@ -34,7 +35,7 @@ func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 { return ScrubStrategyIPV6Lowest32 } - if e.GDPR || e.CCPA { + if e.GDPR || e.CCPA || e.LMT { return ScrubStrategyIPV6Lowest16 } @@ -46,7 +47,7 @@ func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { return ScrubStrategyGeoFull } - if e.GDPRGeo || e.CCPA { + if e.GDPRGeo || e.CCPA || e.LMT { return ScrubStrategyGeoReducedPrecision } @@ -63,7 +64,7 @@ func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUs } // If no user scrubbing is needed, then return none, else scrub ID (COPPA checked above) - if e.CCPA || e.GDPR { + if e.CCPA || e.GDPR || e.LMT { return ScrubStrategyUserID } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index 0e82648d4b9..968c6354710 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -21,6 +21,7 @@ func TestAny(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + LMT: false, }, expected: false, }, @@ -31,6 +32,7 @@ func TestAny(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + LMT: true, }, expected: true, }, @@ -41,16 +43,7 @@ func TestAny(t *testing.T) { COPPA: true, GDPR: false, GDPRGeo: false, - }, - expected: true, - }, - { - description: "GDPRGeo only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPR: false, - GDPRGeo: true, + LMT: true, }, expected: true, }, @@ -79,6 +72,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + LMT: true, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -93,6 +87,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + LMT: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -107,6 +102,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: false, GDPRGeo: false, + LMT: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -121,6 +117,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: true, + LMT: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -135,6 +132,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: true, + LMT: false, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -149,6 +147,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + LMT: false, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -163,6 +162,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + LMT: false, }, ampGDPRException: true, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, @@ -177,6 +177,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: false, + LMT: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, @@ -191,6 +192,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: true, + LMT: false, }, ampGDPRException: false, expectedDeviceIPv6: ScrubStrategyIPV6None, @@ -198,6 +200,36 @@ func TestApply(t *testing.T) { expectedUser: ScrubStrategyUserNone, expectedUserGeo: ScrubStrategyGeoReducedPrecision, }, + { + description: "LMT Only", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: false, + LMT: true, + }, + ampGDPRException: false, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedUser: ScrubStrategyUserID, + expectedUserGeo: ScrubStrategyGeoReducedPrecision, + }, + { + description: "LMT Only, ampGDPRException", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: false, + LMT: true, + }, + ampGDPRException: true, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedUser: ScrubStrategyUserID, + expectedUserGeo: ScrubStrategyGeoReducedPrecision, + }, } for _, test := range testCases { @@ -229,6 +261,7 @@ func TestApplyNoneApplicable(t *testing.T) { CCPA: false, COPPA: false, GDPR: false, + LMT: false, } enforcement.apply(req, false, m) diff --git a/privacy/lmt/policy.go b/privacy/lmt/policy.go new file mode 100644 index 00000000000..79425bf59f7 --- /dev/null +++ b/privacy/lmt/policy.go @@ -0,0 +1,33 @@ +package lmt + +import ( + "github.com/mxmCherry/openrtb" +) + +const ( + trackingUnrestricted = 0 + trackingRestricted = 1 +) + +// Policy represents the LMT (Limit Ad Tracking) policy for an OpenRTB bid request. +type Policy struct { + Signal int + SignalProvided bool +} + +// ReadPolicy extracts the LMT (Limit Ad Tracking) policy from an OpenRTB bid request. +func ReadPolicy(req *openrtb.BidRequest) Policy { + policy := Policy{} + + if req != nil && req.Device != nil && req.Device.Lmt != nil { + policy.Signal = int(*req.Device.Lmt) + policy.SignalProvided = true + } + + return policy +} + +// ShouldEnforce returns true when the LMT (Limit Ad Tracking) policy is in effect. +func (p Policy) ShouldEnforce() bool { + return p.SignalProvided && p.Signal == trackingRestricted +} diff --git a/privacy/lmt/policy_test.go b/privacy/lmt/policy_test.go new file mode 100644 index 00000000000..45de219a9bf --- /dev/null +++ b/privacy/lmt/policy_test.go @@ -0,0 +1,128 @@ +package lmt + +import ( + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/stretchr/testify/assert" +) + +func TestRead(t *testing.T) { + var one int8 = 1 + + testCases := []struct { + description string + request *openrtb.BidRequest + expectedPolicy Policy + }{ + { + description: "Nil Request", + request: nil, + expectedPolicy: Policy{ + Signal: 0, + SignalProvided: false, + }, + }, + { + description: "Nil Device", + request: &openrtb.BidRequest{ + Device: nil, + }, + expectedPolicy: Policy{ + Signal: 0, + SignalProvided: false, + }, + }, + { + description: "Nil Device.Lmt", + request: &openrtb.BidRequest{ + Device: &openrtb.Device{ + Lmt: nil, + }, + }, + expectedPolicy: Policy{ + Signal: 0, + SignalProvided: false, + }, + }, + { + description: "Enabled", + request: &openrtb.BidRequest{ + Device: &openrtb.Device{ + Lmt: &one, + }, + }, + expectedPolicy: Policy{ + Signal: 1, + SignalProvided: true, + }, + }, + } + + for _, test := range testCases { + p := ReadPolicy(test.request) + assert.Equal(t, test.expectedPolicy, p, test.description) + } +} + +func TestShouldEnforce(t *testing.T) { + testCases := []struct { + description string + policy Policy + expected bool + }{ + { + description: "Signal Not Provided - Zero", + policy: Policy{ + Signal: 0, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Not Provided - One", + policy: Policy{ + Signal: 1, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Not Provided - Other", + policy: Policy{ + Signal: 42, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Provided - Zero", + policy: Policy{ + Signal: 0, + SignalProvided: true, + }, + expected: false, + }, + { + description: "Signal Provided - One", + policy: Policy{ + Signal: 1, + SignalProvided: true, + }, + expected: true, + }, + { + description: "Signal Provided - Other", + policy: Policy{ + Signal: 42, + SignalProvided: true, + }, + expected: false, + }, + } + + for _, test := range testCases { + result := test.policy.ShouldEnforce() + assert.Equal(t, test.expected, result, test.description) + } +} From dd05c38f5b698441b8f5c07908506093b2290745 Mon Sep 17 00:00:00 2001 From: Richard Lee <14349+dlackty@users.noreply.github.com> Date: Mon, 15 Jun 2020 23:21:03 +0800 Subject: [PATCH 114/318] Avoid overriding AMP request original size with mutli-size (#1352) --- endpoints/openrtb2/amp_auction.go | 17 ++++++++++------- endpoints/openrtb2/amp_auction_test.go | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 586481ddfc5..2dcd572c63c 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -407,31 +407,34 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope } func makeFormatReplacement(overrideWidth uint64, overrideHeight uint64, width uint64, height uint64, multisize string) []openrtb.Format { + var formats []openrtb.Format if overrideWidth != 0 && overrideHeight != 0 { - return []openrtb.Format{{ + formats = []openrtb.Format{{ W: overrideWidth, H: overrideHeight, }} } else if overrideWidth != 0 && height != 0 { - return []openrtb.Format{{ + formats = []openrtb.Format{{ W: overrideWidth, H: height, }} } else if width != 0 && overrideHeight != 0 { - return []openrtb.Format{{ + formats = []openrtb.Format{{ W: width, H: overrideHeight, }} - } else if parsedSizes := parseMultisize(multisize); len(parsedSizes) != 0 { - return parsedSizes } else if width != 0 && height != 0 { - return []openrtb.Format{{ + formats = []openrtb.Format{{ W: width, H: height, }} } - return nil + if parsedSizes := parseMultisize(multisize); len(parsedSizes) != 0 { + formats = append(formats, parsedSizes...) + } + + return formats } func setWidths(formats []openrtb.Format, width uint64) { diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 289db3f48cb..731fd55e196 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -832,6 +832,24 @@ func TestMultisize(t *testing.T) { }.execute(t) } +func TestSizeWithMultisize(t *testing.T) { + formatOverrideSpec{ + width: 20, + height: 40, + multisize: "200x50,100x60", + expect: []openrtb.Format{{ + W: 20, + H: 40, + }, { + W: 200, + H: 50, + }, { + W: 100, + H: 60, + }}, + }.execute(t) +} + func TestHeightOnly(t *testing.T) { formatOverrideSpec{ height: 200, From 62fe413dad59ee0c6c95e410ae6ad26d8af304ea Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 17 Jun 2020 10:25:02 -0400 Subject: [PATCH 115/318] Extra logging for timeout notifications (#1349) --- exchange/bidder.go | 13 ++++++++ exchange/bidder_test.go | 74 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/exchange/bidder.go b/exchange/bidder.go index f9b4a522343..df9f0a3bf1b 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -389,7 +389,20 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou } } else { bidder.me.RecordTimeoutNotice(false) + if bidder.DebugConfig.TimeoutNotification.Log { + msg := fmt.Sprintf("TimeoutNotification: Failed to make timeout request: method(%s), uri(%s), error(%s)", toReq.Method, toReq.Uri, err.Error()) + util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) + } + } + } else if bidder.DebugConfig.TimeoutNotification.Log { + reqJSON, err := json.Marshal(req) + var msg string + if err == nil { + msg = fmt.Sprintf("TimeoutNotification: Failed to generate timeout request: error(%s), bidder request(%s)", errL[0].Error(), string(reqJSON)) + } else { + msg = fmt.Sprintf("TimeoutNotification: Failed to generate timeout request: error(%s), bidder request marshal failed(%s)", errL[0].Error(), err.Error()) } + util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) } } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index fa04e6a4771..fff397f0084 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -1229,6 +1229,64 @@ func TestSetAssetTypes(t *testing.T) { } } +func TestTimeoutNotificationOff(t *testing.T) { + respBody := "{\"bid\":false}" + respStatus := 200 + server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) + defer server.Close() + + bidderImpl := ¬ifingBidder{ + notiRequest: adapters.RequestData{ + Method: "GET", + Uri: server.URL + "/notify/me", + Body: nil, + Headers: http.Header{}, + }, + } + bidder := &bidderAdapter{ + Bidder: bidderImpl, + Client: server.Client(), + DebugConfig: config.Debug{}, + me: &metricsConfig.DummyMetricsEngine{}, + } + if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok { + t.Error("Failed to cast bidder to a TimeoutBidder") + } else { + bidder.doTimeoutNotification(tb, &adapters.RequestData{}) + } +} + +func TestTimeoutNotificationOn(t *testing.T) { + respBody := "{\"bid\":false}" + respStatus := 200 + server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) + defer server.Close() + + bidderImpl := ¬ifingBidder{ + notiRequest: adapters.RequestData{ + Method: "GET", + Uri: server.URL + "/notify/me", + Body: nil, + Headers: http.Header{}, + }, + } + bidder := &bidderAdapter{ + Bidder: bidderImpl, + Client: server.Client(), + DebugConfig: config.Debug{ + TimeoutNotification: config.TimeoutNotification{ + Log: true, + }, + }, + me: &metricsConfig.DummyMetricsEngine{}, + } + if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok { + t.Error("Failed to cast bidder to a TimeoutBidder") + } else { + bidder.doTimeoutNotification(tb, &adapters.RequestData{}) + } +} + type goodSingleBidder struct { bidRequest *openrtb.BidRequest httpRequest *adapters.RequestData @@ -1302,3 +1360,19 @@ func (bidder *bidRejector) MakeBids(internalRequest *openrtb.BidRequest, externa bidder.httpResponse = response return nil, []error{errors.New("Can't make a response.")} } + +type notifingBidder struct { + notiRequest adapters.RequestData +} + +func (bidder *notifingBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + return nil, nil +} + +func (bidder *notifingBidder) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + return nil, nil +} + +func (bidder *notifingBidder) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error) { + return &bidder.notiRequest, nil +} From 2d2ed0c6dcd984769d1a65edc15a96bfc4c69482 Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Wed, 17 Jun 2020 18:32:47 +0100 Subject: [PATCH 116/318] Consumable: Correct bid type, should always be "banner". (#1359) --- adapters/consumable/consumable.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go index 1fa23377319..243f1b8000b 100644 --- a/adapters/consumable/consumable.go +++ b/adapters/consumable/consumable.go @@ -268,8 +268,11 @@ func (a *ConsumableAdapter) MakeBids( //bid.referrer = utils.getTopWindowUrl(); bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: getMediaTypeForImp(getImp(bid.ImpID, internalRequest.Imp)), + Bid: &bid, + // Consumable units are always HTML, never VAST. + // From Prebid's point of view, this means that Consumable units + // are always "banners". + BidType: openrtb_ext.BidTypeBanner, }) } } @@ -303,16 +306,6 @@ func extractExtensions(impression openrtb.Imp) (*adapters.ExtImpBidder, *openrtb return &bidderExt, &consumableExt, nil } -func getMediaTypeForImp(imp *openrtb.Imp) openrtb_ext.BidType { - // TODO: Whatever logic we need here possibly as follows - may always be Video when we bid - if imp.Banner != nil { - return openrtb_ext.BidTypeBanner - } else if imp.Video != nil { - return openrtb_ext.BidTypeVideo - } - return openrtb_ext.BidTypeVideo -} - func testConsumableBidder(testClock instant, endpoint string) *ConsumableAdapter { return &ConsumableAdapter{testClock, endpoint} } From 98417cb13f3181a1cfd1d1b8089ab17de3423467 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 17 Jun 2020 13:33:22 -0400 Subject: [PATCH 117/318] Build With Go 1.14 (#1350) --- .travis.yml | 3 +-- Dockerfile | 4 ++-- README.md | 5 +++-- go.mod | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea2c46c4374..692141f716c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ language: go go: - - '1.12' - '1.13' - - '1.14' + - '1.14.2' go_import_path: github.com/prebid/prebid-server diff --git a/Dockerfile b/Dockerfile index a8fea9c33f6..2c60b9e39b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ RUN apt-get update && \ apt-get -y upgrade && \ apt-get install -y wget RUN cd /tmp && \ - wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz && \ - tar -xf go1.12.7.linux-amd64.tar.gz && \ + wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz && \ + tar -xf go1.14.2.linux-amd64.tar.gz && \ mv go /usr/local RUN mkdir -p /app/prebid-server/ WORKDIR /app/prebid-server/ diff --git a/README.md b/README.md index a59bf5f6aa3..b69e7e76db4 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ For more information, see: ## Installation -First install [Go 1.12](https://golang.org/doc/install) latest version. +First install [Go](https://golang.org/doc/install) version 1.13 or newer. + Note that prebid-server is using [Go modules](https://blog.golang.org/using-go-modules). -If using Go version <1.13 and are inside GOPATH `GO111MODULE` needs to be set to `GO111MODULE=on`. +We officially support the most recent two major versions of the Go runtime. However, if you'd like to use a version <1.13 and are inside GOPATH `GO111MODULE` needs to be set to `GO111MODULE=on`. Download and prepare Prebid Server: diff --git a/go.mod b/go.mod index 0224057e464..72bb9b74886 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/prebid/prebid-server -go 1.12 +go 1.13 require ( github.com/BurntSushi/toml v0.3.1 // indirect From d1c81294a7d05bb626c9b8f8181d845b8cee7fe9 Mon Sep 17 00:00:00 2001 From: jmaynardxandr <46759873+jmaynardxandr@users.noreply.github.com> Date: Wed, 17 Jun 2020 11:24:33 -0700 Subject: [PATCH 118/318] Category mapping changes from product team. (#1348) --- static/adapter/appnexus/opts.json | 69 +- .../category-mapping/freewheel/freewheel.json | 2348 +++++++++-------- 2 files changed, 1234 insertions(+), 1183 deletions(-) diff --git a/static/adapter/appnexus/opts.json b/static/adapter/appnexus/opts.json index 7bb297e0b41..41ee3c8f313 100644 --- a/static/adapter/appnexus/opts.json +++ b/static/adapter/appnexus/opts.json @@ -5,13 +5,14 @@ "3": "IAB10-1", "4": "IAB2-3", "5": "IAB19-8", + "6": "IAB22-1", "7": "IAB18-1", - "8": "IAB14-1", + "8": "IAB12-3", "9": "IAB5-1", "10": "IAB4-5", "11": "IAB13-4", - "13": "IAB19-2", "12": "IAB8-7", + "13": "IAB9-7", "14": "IAB7-1", "15": "IAB20-18", "16": "IAB10-7", @@ -20,33 +21,79 @@ "19": "IAB18-4", "20": "IAB1-5", "21": "IAB1-6", - "22": "IAB19-28", + "22": "IAB3-4", "23": "IAB19-13", "24": "IAB22-2", "25": "IAB3-9", - "26": "IAB17-26", + "26": "IAB17-18", "27": "IAB19-6", "28": "IAB1-7", - "29": "IAB9-5", + "29": "IAB9-30", "30": "IAB20-7", "31": "IAB20-17", "32": "IAB7-32", "33": "IAB16-5", "34": "IAB19-34", + "35": "IAB11-5", + "36": "IAB12-3", "37": "IAB11-4", + "38": "IAB12-3", "39": "IAB9-30", "41": "IAB7-44", + "42": "IAB7-1", + "43": "IAB7-30", + "50": "IAB19-30", "51": "IAB17-12", + "52": "IAB19-30", "53": "IAB3-1", "55": "IAB13-2", + "56": "IAB19-30", + "57": "IAB19-30", + "58": "IAB7-39", + "59": "IAB22-1", + "60": "IAB7-39", "61": "IAB21-3", - "62": "IAB6-4", - "63": "IAB15-10", + "62": "IAB5-1", + "63": "IAB12-3", + "64": "IAB20-18", "65": "IAB11-2", + "66": "IAB17-18", "67": "IAB9-9", - "69": "IAB7-1", - "71": "IAB22-2", + "68": "IAB9-5", + "69": "IAB7-44", + "71": "IAB22-3", + "73": "IAB19-30", "74": "IAB8-5", - "87": "IAB3-7" - } + "78": "IAB22-1", + "85": "IAB12-2", + "86": "IAB22-3", + "87": "IAB11-3", + "112": "IAB7-32", + "113": "IAB7-32", + "114": "IAB7-32", + "115": "IAB7-32", + "118": "IAB9-5", + "119": "IAB9-5", + "120": "IAB9-5", + "121": "IAB9-5", + "122": "IAB9-5", + "123": "IAB9-5", + "124": "IAB9-5", + "125": "IAB9-5", + "126": "IAB9-5", + "127": "IAB22-1", + "132": "IAB1-2", + "133": "IAB19-30", + "137": "IAB3-9", + "138": "IAB19-3", + "140": "IAB2-3", + "141": "IAB2-1", + "142": "IAB2-3", + "143": "IAB17-13", + "166": "IAB11-4", + "175": "IAB3-1", + "176": "IAB13-4", + "182": "IAB8-9", + "183": "IAB3-5" + } } diff --git a/static/category-mapping/freewheel/freewheel.json b/static/category-mapping/freewheel/freewheel.json index 7eebcce0c98..1c4a4fa2471 100644 --- a/static/category-mapping/freewheel/freewheel.json +++ b/static/category-mapping/freewheel/freewheel.json @@ -3,1176 +3,1180 @@ "id": "404", "name": "Publishing" }, - "IAB1-2": { - "id": "392", - "name": "Entertainment" - }, - "IAB1-5": { - "id": "419", - "name": "Filmed Entertainment" - }, - "IAB1-6": { - "id": "392", - "name": "Entertainment" - }, - "IAB1-7": { - "id": "392", - "name": "Entertainment" - }, - "IAB2-1": { - "id": "399", - "name": "Automotive" - }, - "IAB2-2": { - "id": "399", - "name": "Automotive" - }, - "IAB2-3": { - "id": "399", - "name": "Automotive" - }, - "IAB2-4": { - "id": "399", - "name": "Automotive" - }, - "IAB2-5": { - "id": "399", - "name": "Automotive" - }, - "IAB2-6": { - "id": "399", - "name": "Automotive" - }, - "IAB2-7": { - "id": "399", - "name": "Automotive" - }, - "IAB2-8": { - "id": "399", - "name": "Automotive" - }, - "IAB2-9": { - "id": "399", - "name": "Automotive" - }, - "IAB2-10": { - "id": "399", - "name": "Automotive" - }, - "IAB2-11": { - "id": "399", - "name": "Automotive" - }, - "IAB2-12": { - "id": "399", - "name": "Automotive" - }, - "IAB2-13": { - "id": "399", - "name": "Automotive" - }, - "IAB2-14": { - "id": "399", - "name": "Automotive" - }, - "IAB2-15": { - "id": "399", - "name": "Automotive" - }, - "IAB2-16": { - "id": "399", - "name": "Automotive" - }, - "IAB2-17": { - "id": "399", - "name": "Automotive" - }, - "IAB2-18": { - "id": "399", - "name": "Automotive" - }, - "IAB2-19": { - "id": "399", - "name": "Automotive" - }, - "IAB2-20": { - "id": "399", - "name": "Automotive" - }, - "IAB2-21": { - "id": "399", - "name": "Automotive" - }, - "IAB2-22": { - "id": "399", - "name": "Automotive" - }, - "IAB2-23": { - "id": "399", - "name": "Automotive" - }, - "IAB3-1": { - "id": "393", - "name": "Business Services" - }, - "IAB3-2": { - "id": "393", - "name": "Business Services" - }, - "IAB3-3": { - "id": "393", - "name": "Business Services" - }, - "IAB3-4": { - "id": "409", - "name": "Computing Product" - }, - "IAB3-5": { - "id": "393", - "name": "Business Services" - }, - "IAB3-6": { - "id": "393", - "name": "Business Services" - }, - "IAB3-7": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB3-8": { - "id": "393", - "name": "Business Services" - }, - "IAB3-9": { - "id": "393", - "name": "Business Services" - }, - "IAB3-10": { - "id": "393", - "name": "Business Services" - }, - "IAB3-11": { - "id": "393", - "name": "Business Services" - }, - "IAB3-12": { - "id": "393", - "name": "Business Services" - }, - "IAB4-1": { - "id": "393", - "name": "Business Services" - }, - "IAB4-2": { - "id": "405", - "name": "Educational Services" - }, - "IAB4-3": { - "id": "405", - "name": "Educational Services" - }, - "IAB4-4": { - "id": "393", - "name": "Business Services" - }, - "IAB4-5": { - "id": "393", - "name": "Business Services" - }, - "IAB4-6": { - "id": "393", - "name": "Business Services" - }, - "IAB4-7": { - "id": "406", - "name": "Health Care Services" - }, - "IAB4-8": { - "id": "405", - "name": "Educational Services" - }, - "IAB4-9": { - "id": "417", - "name": "Telecommunications" - }, - "IAB4-10": { - "id": "429", - "name": "Military" - }, - "IAB4-11": { - "id": "393", - "name": "Business Services" - }, - "IAB5-1": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-2": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-3": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-4": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-5": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-6": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-7": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-8": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-9": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-10": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-11": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-12": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-13": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-14": { - "id": "405", - "name": "Educational Services" - }, - "IAB5-15": { - "id": "405", - "name": "Educational Services" - }, - "IAB7-1": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-2": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-3": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-4": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-5": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-6": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-7": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-8": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-9": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-10": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-11": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-12": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-13": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-14": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-15": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-16": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-17": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-18": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-19": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-20": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-21": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-22": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-23": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-24": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-25": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-26": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-27": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-28": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-29": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-30": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-31": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-32": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-33": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-34": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-35": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-36": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-37": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-38": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-39": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-40": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-41": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-42": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-43": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-44": { - "id": "406", - "name": "Health Care Services" - }, - "IAB7-45": { - "id": "406", - "name": "Health Care Services" - }, - "IAB8-1": { - "id": "394", - "name": "Food" - }, - "IAB8-2": { - "id": "394", - "name": "Food" - }, - "IAB8-3": { - "id": "394", - "name": "Food" - }, - "IAB8-4": { - "id": "394", - "name": "Food" - }, - "IAB8-5": { - "id": "400", - "name": "Beer/Wine/Liquor" - }, - "IAB8-6": { - "id": "401", - "name": "Beverages" - }, - "IAB8-7": { - "id": "394", - "name": "Food" - }, - "IAB8-8": { - "id": "394", - "name": "Food" - }, - "IAB8-9": { - "id": "407", - "name": "Restaurant/Fast Food" - }, - "IAB8-10": { - "id": "394", - "name": "Food" - }, - "IAB8-11": { - "id": "394", - "name": "Food" - }, - "IAB8-12": { - "id": "394", - "name": "Food" - }, - "IAB8-13": { - "id": "394", - "name": "Food" - }, - "IAB8-14": { - "id": "394", - "name": "Food" - }, - "IAB8-15": { - "id": "394", - "name": "Food" - }, - "IAB8-16": { - "id": "394", - "name": "Food" - }, - "IAB8-17": { - "id": "394", - "name": "Food" - }, - "IAB8-18": { - "id": "400", - "name": "Beer/Wine/Liquor" - }, - "IAB9-1": { - "id": "392", - "name": "Entertainment" - }, - "IAB9-3": { - "id": "418", - "name": "Jewelry" - }, - "IAB9-5": { - "id": "413", - "name": "Gaming" - }, - "IAB9-6": { - "id": "412", - "name": "Household Products" - }, - "IAB9-9": { - "id": "426", - "name": "Tobacco" - }, - "IAB9-11": { - "id": "404", - "name": "Publishing" - }, - "IAB9-15": { - "id": "404", - "name": "Publishing" - }, - "IAB9-16": { - "id": "392", - "name": "Entertainment" - }, - "IAB9-18": { - "id": "393", - "name": "Business Services" - }, - "IAB9-19": { - "id": "418", - "name": "Jewelry" - }, - "IAB9-23": { - "id": "424", - "name": "Photographic Equipment" - }, - "IAB9-24": { - "id": "392", - "name": "Entertainment" - }, - "IAB9-25": { - "id": "392", - "name": "Entertainment" - }, - "IAB9-30": { - "id": "413", - "name": "Gaming" - }, - "IAB10-1": { - "id": "415", - "name": "Appliances" - }, - "IAB10-5": { - "id": "434", - "name": "Home Furnishings" - }, - "IAB10-6": { - "id": "434", - "name": "Home Furnishings" - }, - "IAB10-7": { - "id": "434", - "name": "Home Furnishings" - }, - "IAB10-8": { - "id": "393", - "name": "Business Services" - }, - "IAB10-9": { - "id": "434", - "name": "Home Furnishings" - }, - "IAB11-1": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB11-2": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB11-3": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB11-4": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB11-5": { - "id": "398", - "name": "Government/Municipal" - }, - "IAB12-1": { - "id": "438", - "name": "News" - }, - "IAB12-2": { - "id": "438", - "name": "News" - }, - "IAB12-3": { - "id": "438", - "name": "News" - }, - "IAB13-1": { - "id": "393", - "name": "Business Services" - }, - "IAB13-2": { - "id": "393", - "name": "Business Services" - }, - "IAB13-3": { - "id": "438", - "name": "News" - }, - "IAB13-4": { - "id": "391", - "name": "Financial Services" - }, - "IAB13-5": { - "id": "393", - "name": "Business Services" - }, - "IAB13-6": { - "id": "436", - "name": "Insurance" - }, - "IAB13-7": { - "id": "393", - "name": "Business Services" - }, - "IAB13-8": { - "id": "393", - "name": "Business Services" - }, - "IAB13-9": { - "id": "393", - "name": "Business Services" - }, - "IAB13-10": { - "id": "393", - "name": "Business Services" - }, - "IAB13-11": { - "id": "393", - "name": "Business Services" - }, - "IAB13-12": { - "id": "393", - "name": "Business Services" - }, - "IAB16-1": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-2": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-3": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-4": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-5": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-6": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB16-7": { - "id": "423", - "name": "Pet Food/Supplies" - }, - "IAB17-1": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-2": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-3": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-4": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-5": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-6": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-7": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-8": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-9": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-10": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-11": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-12": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-13": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-14": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-15": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-16": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-17": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-18": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-19": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-20": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-21": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-22": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-23": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-24": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-25": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-26": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-27": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-28": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-29": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-30": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-31": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-32": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-33": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-34": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-35": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-36": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-37": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-38": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-39": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-40": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-41": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-42": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-43": { - "id": "425", - "name": "Professional Sports" - }, - "IAB17-44": { - "id": "425", - "name": "Professional Sports" - }, - "IAB18-1": { - "id": "411", - "name": "Cosmetics/Toiletries" - }, - "IAB18-2": { - "id": "397", - "name": "Apparel" - }, - "IAB18-3": { - "id": "397", - "name": "Apparel" - }, - "IAB18-4": { - "id": "418", - "name": "Jewelry" - }, - "IAB18-5": { - "id": "397", - "name": "Apparel" - }, - "IAB18-6": { - "id": "397", - "name": "Apparel" - }, - "IAB19-2": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-3": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-4": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-5": { - "id": "424", - "name": "Photographic Equipment" - }, - "IAB19-6": { - "id": "417", - "name": "Telecommunications" - }, - "IAB19-7": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-8": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-9": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-10": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-11": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-12": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-13": { - "id": "404", - "name": "Publishing" - }, - "IAB19-14": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-15": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-16": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-17": { - "id": "419", - "name": "Filmed Entertainment" - }, - "IAB19-18": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-19": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-20": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-21": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-22": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-23": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-24": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-25": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-26": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-27": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-28": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-29": { - "id": "392", - "name": "Entertainment" - }, - "IAB19-30": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-31": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-32": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-33": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-34": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-35": { - "id": "409", - "name": "Computing Product" - }, - "IAB19-36": { - "id": "409", - "name": "Computing Product" - }, - "IAB20-1": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-2": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-3": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-4": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-5": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-6": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-7": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-8": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-9": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-10": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-11": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-12": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-13": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-14": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-15": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-16": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-17": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-18": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-19": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-20": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-21": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-22": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-23": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-24": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-25": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-26": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB20-27": { - "id": "395", - "name": "Travel/Hotels/Airlines" - }, - "IAB21-1": { - "id": "416", - "name": "Real Estate" - }, - "IAB21-2": { - "id": "416", - "name": "Real Estate" - }, - "IAB21-3": { - "id": "416", - "name": "Real Estate" - }, - "IAB22-1": { - "id": "416", - "name": "Real Estate" - }, - "IAB22-2": { - "id": "416", - "name": "Real Estate" - }, - "IAB22-3": { - "id": "416", - "name": "Real Estate" - } + "IAB1-2": { + "id": "392", + "name": "Entertainment" + }, + "IAB1-5": { + "id": "419", + "name": "Filmed Entertainment" + }, + "IAB1-6": { + "id": "392", + "name": "Entertainment" + }, + "IAB1-7": { + "id": "392", + "name": "Entertainment" + }, + "IAB2-1": { + "id": "399", + "name": "Automotive" + }, + "IAB2-2": { + "id": "399", + "name": "Automotive" + }, + "IAB2-3": { + "id": "399", + "name": "Automotive" + }, + "IAB2-4": { + "id": "399", + "name": "Automotive" + }, + "IAB2-5": { + "id": "399", + "name": "Automotive" + }, + "IAB2-6": { + "id": "399", + "name": "Automotive" + }, + "IAB2-7": { + "id": "399", + "name": "Automotive" + }, + "IAB2-8": { + "id": "399", + "name": "Automotive" + }, + "IAB2-9": { + "id": "399", + "name": "Automotive" + }, + "IAB2-10": { + "id": "399", + "name": "Automotive" + }, + "IAB2-11": { + "id": "399", + "name": "Automotive" + }, + "IAB2-12": { + "id": "399", + "name": "Automotive" + }, + "IAB2-13": { + "id": "399", + "name": "Automotive" + }, + "IAB2-14": { + "id": "399", + "name": "Automotive" + }, + "IAB2-15": { + "id": "399", + "name": "Automotive" + }, + "IAB2-16": { + "id": "399", + "name": "Automotive" + }, + "IAB2-17": { + "id": "399", + "name": "Automotive" + }, + "IAB2-18": { + "id": "399", + "name": "Automotive" + }, + "IAB2-19": { + "id": "399", + "name": "Automotive" + }, + "IAB2-20": { + "id": "399", + "name": "Automotive" + }, + "IAB2-21": { + "id": "399", + "name": "Automotive" + }, + "IAB2-22": { + "id": "399", + "name": "Automotive" + }, + "IAB2-23": { + "id": "399", + "name": "Automotive" + }, + "IAB3-1": { + "id": "393", + "name": "Business Services" + }, + "IAB3-2": { + "id": "393", + "name": "Business Services" + }, + "IAB3-3": { + "id": "393", + "name": "Business Services" + }, + "IAB3-4": { + "id": "408", + "name": "Office Equipment/Supplies" + }, + "IAB3-5": { + "id": "390", + "name": "Manufacturing" + }, + "IAB3-6": { + "id": "393", + "name": "Business Services" + }, + "IAB3-7": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB3-8": { + "id": "393", + "name": "Business Services" + }, + "IAB3-9": { + "id": "393", + "name": "Business Services" + }, + "IAB3-10": { + "id": "393", + "name": "Business Services" + }, + "IAB3-11": { + "id": "393", + "name": "Business Services" + }, + "IAB3-12": { + "id": "393", + "name": "Business Services" + }, + "IAB4-1": { + "id": "393", + "name": "Business Services" + }, + "IAB4-2": { + "id": "405", + "name": "Educational Services" + }, + "IAB4-3": { + "id": "405", + "name": "Educational Services" + }, + "IAB4-4": { + "id": "393", + "name": "Business Services" + }, + "IAB4-5": { + "id": "393", + "name": "Business Services" + }, + "IAB4-6": { + "id": "393", + "name": "Business Services" + }, + "IAB4-7": { + "id": "406", + "name": "Health Care Services" + }, + "IAB4-8": { + "id": "405", + "name": "Educational Services" + }, + "IAB4-9": { + "id": "417", + "name": "Telecommunications" + }, + "IAB4-10": { + "id": "429", + "name": "Military" + }, + "IAB4-11": { + "id": "393", + "name": "Business Services" + }, + "IAB5-1": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-2": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-3": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-4": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-5": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-6": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-7": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-8": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-9": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-10": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-11": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-12": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-13": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-14": { + "id": "405", + "name": "Educational Services" + }, + "IAB5-15": { + "id": "405", + "name": "Educational Services" + }, + "IAB7-1": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-2": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-3": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-4": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-5": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-6": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-7": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-8": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-9": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-10": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-11": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-12": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-13": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-14": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-15": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-16": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-17": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-18": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-19": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-20": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-21": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-22": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-23": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-24": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-25": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-26": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-27": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-28": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-29": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-30": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-31": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-32": { + "id": "402", + "name": "Pharmaceuticals" + }, + "IAB7-33": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-34": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-35": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-36": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-37": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-38": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-39": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-40": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-41": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-42": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-43": { + "id": "406", + "name": "Health Care Services" + }, + "IAB7-44": { + "id": "433", + "name": "Drug Stores" + }, + "IAB7-45": { + "id": "406", + "name": "Health Care Services" + }, + "IAB8-1": { + "id": "394", + "name": "Food" + }, + "IAB8-2": { + "id": "394", + "name": "Food" + }, + "IAB8-3": { + "id": "394", + "name": "Food" + }, + "IAB8-4": { + "id": "394", + "name": "Food" + }, + "IAB8-5": { + "id": "400", + "name": "Beer/Wine/Liquor" + }, + "IAB8-6": { + "id": "401", + "name": "Beverages" + }, + "IAB8-7": { + "id": "394", + "name": "Food" + }, + "IAB8-8": { + "id": "394", + "name": "Food" + }, + "IAB8-9": { + "id": "407", + "name": "Restaurant/Fast Food" + }, + "IAB8-10": { + "id": "394", + "name": "Food" + }, + "IAB8-11": { + "id": "394", + "name": "Food" + }, + "IAB8-12": { + "id": "394", + "name": "Food" + }, + "IAB8-13": { + "id": "394", + "name": "Food" + }, + "IAB8-14": { + "id": "394", + "name": "Food" + }, + "IAB8-15": { + "id": "394", + "name": "Food" + }, + "IAB8-16": { + "id": "394", + "name": "Food" + }, + "IAB8-17": { + "id": "394", + "name": "Food" + }, + "IAB8-18": { + "id": "400", + "name": "Beer/Wine/Liquor" + }, + "IAB9-1": { + "id": "392", + "name": "Entertainment" + }, + "IAB9-3": { + "id": "418", + "name": "Jewelry" + }, + "IAB9-5": { + "id": "414", + "name": "Gambling" + }, + "IAB9-6": { + "id": "412", + "name": "Household Products" + }, + "IAB9-7": { + "id": "413", + "name": "Gaming" + }, + "IAB9-9": { + "id": "426", + "name": "Tobacco" + }, + "IAB9-11": { + "id": "404", + "name": "Publishing" + }, + "IAB9-15": { + "id": "404", + "name": "Publishing" + }, + "IAB9-16": { + "id": "392", + "name": "Entertainment" + }, + "IAB9-18": { + "id": "393", + "name": "Business Services" + }, + "IAB9-19": { + "id": "418", + "name": "Jewelry" + }, + "IAB9-23": { + "id": "424", + "name": "Photographic Equipment" + }, + "IAB9-24": { + "id": "392", + "name": "Entertainment" + }, + "IAB9-25": { + "id": "392", + "name": "Entertainment" + }, + "IAB9-30": { + "id": "427", + "name": "Toys/Games" + }, + "IAB10-1": { + "id": "415", + "name": "Appliances" + }, + "IAB10-5": { + "id": "434", + "name": "Home Furnishings" + }, + "IAB10-6": { + "id": "434", + "name": "Home Furnishings" + }, + "IAB10-7": { + "id": "434", + "name": "Home Furnishings" + }, + "IAB10-8": { + "id": "393", + "name": "Business Services" + }, + "IAB10-9": { + "id": "434", + "name": "Home Furnishings" + }, + "IAB11-1": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB11-2": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB11-3": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB11-4": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB11-5": { + "id": "421", + "name": "Associations" + }, + "IAB12-1": { + "id": "438", + "name": "News" + }, + "IAB12-2": { + "id": "438", + "name": "News" + }, + "IAB12-3": { + "id": "438", + "name": "News" + }, + "IAB13-1": { + "id": "393", + "name": "Business Services" + }, + "IAB13-2": { + "id": "393", + "name": "Business Services" + }, + "IAB13-3": { + "id": "438", + "name": "News" + }, + "IAB13-4": { + "id": "391", + "name": "Financial Services" + }, + "IAB13-5": { + "id": "393", + "name": "Business Services" + }, + "IAB13-6": { + "id": "436", + "name": "Insurance" + }, + "IAB13-7": { + "id": "393", + "name": "Business Services" + }, + "IAB13-8": { + "id": "393", + "name": "Business Services" + }, + "IAB13-9": { + "id": "393", + "name": "Business Services" + }, + "IAB13-10": { + "id": "393", + "name": "Business Services" + }, + "IAB13-11": { + "id": "393", + "name": "Business Services" + }, + "IAB13-12": { + "id": "393", + "name": "Business Services" + }, + "IAB16-1": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-2": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-3": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-4": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-5": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-6": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB16-7": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB17-1": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-2": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-3": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-4": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-5": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-6": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-7": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-8": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-9": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-10": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-11": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-12": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-13": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-14": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-15": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-16": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-17": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-18": { + "id": "412", + "name": "Household Products" + }, + "IAB17-19": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-20": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-21": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-22": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-23": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-24": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-25": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-26": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-27": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-28": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-29": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-30": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-31": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-32": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-33": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-34": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-35": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-36": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-37": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-38": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-39": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-40": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-41": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-42": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-43": { + "id": "425", + "name": "Professional Sports" + }, + "IAB17-44": { + "id": "425", + "name": "Professional Sports" + }, + "IAB18-1": { + "id": "411", + "name": "Cosmetics/Toiletries" + }, + "IAB18-2": { + "id": "397", + "name": "Apparel" + }, + "IAB18-3": { + "id": "397", + "name": "Apparel" + }, + "IAB18-4": { + "id": "418", + "name": "Jewelry" + }, + "IAB18-5": { + "id": "397", + "name": "Apparel" + }, + "IAB18-6": { + "id": "397", + "name": "Apparel" + }, + "IAB19-2": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-3": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-4": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-5": { + "id": "424", + "name": "Photographic Equipment" + }, + "IAB19-6": { + "id": "417", + "name": "Telecommunications" + }, + "IAB19-7": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-8": { + "id": "432", + "name": "Audio and Video Equipment" + }, + "IAB19-9": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-10": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-11": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-12": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-13": { + "id": "404", + "name": "Publishing" + }, + "IAB19-14": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-15": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-16": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-17": { + "id": "419", + "name": "Filmed Entertainment" + }, + "IAB19-18": { + "id": "431", + "name": "Computing" + }, + "IAB19-19": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-20": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-21": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-22": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-23": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-24": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-25": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-26": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-27": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-28": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-29": { + "id": "392", + "name": "Entertainment" + }, + "IAB19-30": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-31": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-32": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-33": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-34": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-35": { + "id": "409", + "name": "Computing Product" + }, + "IAB19-36": { + "id": "409", + "name": "Computing Product" + }, + "IAB20-1": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-2": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-3": { + "id": "428", + "name": "Aerospace" + }, + "IAB20-4": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-5": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-6": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-7": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-8": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-9": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-10": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-11": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-12": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-13": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-14": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-15": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-16": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-17": { + "id": "396", + "name": "Amusement and Recreation" + }, + "IAB20-18": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-19": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-20": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-21": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-22": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-23": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-24": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-25": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-26": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB20-27": { + "id": "395", + "name": "Travel/Hotels/Airlines" + }, + "IAB21-1": { + "id": "416", + "name": "Real Estate" + }, + "IAB21-2": { + "id": "416", + "name": "Real Estate" + }, + "IAB21-3": { + "id": "416", + "name": "Real Estate" + }, + "IAB22-1": { + "id": "403", + "name": "Retail Stores/Chains" + }, + "IAB22-2": { + "id": "403", + "name": "Retail Stores/Chains" + }, + "IAB22-3": { + "id": "410", + "name": "Product" + } } \ No newline at end of file From 6eed87311b4a5a2f05bcceb758296a6b798f4dfb Mon Sep 17 00:00:00 2001 From: Simon Critchley Date: Thu, 18 Jun 2020 15:08:06 +0100 Subject: [PATCH 119/318] Adds Avocet adapter (#1354) --- adapters/avocet/avocet.go | 124 ++++++++ adapters/avocet/avocet/exemplary/banner.json | 106 +++++++ adapters/avocet/avocet/exemplary/video.json | 104 +++++++ adapters/avocet/avocet_test.go | 301 +++++++++++++++++++ adapters/avocet/usersync.go | 12 + adapters/avocet/usersync_test.go | 35 +++ config/config.go | 2 + docs/bidders/avocet.md | 5 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_avocet.go | 7 + static/bidder-info/avocet.yaml | 11 + static/bidder-params/avocet.json | 24 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 15 files changed, 738 insertions(+) create mode 100644 adapters/avocet/avocet.go create mode 100644 adapters/avocet/avocet/exemplary/banner.json create mode 100644 adapters/avocet/avocet/exemplary/video.json create mode 100644 adapters/avocet/avocet_test.go create mode 100644 adapters/avocet/usersync.go create mode 100644 adapters/avocet/usersync_test.go create mode 100644 docs/bidders/avocet.md create mode 100644 openrtb_ext/imp_avocet.go create mode 100644 static/bidder-info/avocet.yaml create mode 100644 static/bidder-params/avocet.json diff --git a/adapters/avocet/avocet.go b/adapters/avocet/avocet.go new file mode 100644 index 00000000000..918fc23e894 --- /dev/null +++ b/adapters/avocet/avocet.go @@ -0,0 +1,124 @@ +package avocet + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// AvocetAdapter implements a adapters.Bidder compatible with the Avocet advertising platform. +type AvocetAdapter struct { + // Endpoint is a http endpoint to use when making requests to the Avocet advertising platform. + Endpoint string +} + +func (a *AvocetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, nil + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + body, err := json.Marshal(request) + if err != nil { + return nil, []error{&errortypes.FailedToRequestBids{ + Message: err.Error(), + }} + } + reqData := &adapters.RequestData{ + Method: http.MethodPost, + Uri: a.Endpoint, + Body: body, + Headers: headers, + } + return []*adapters.RequestData{reqData}, nil +} + +type avocetBidExt struct { + Avocet avocetBidExtension `json:"avocet"` +} + +type avocetBidExtension struct { + Duration int `json:"duration"` + DealPriority int `json:"deal_priority"` +} + +func (a *AvocetAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode != http.StatusOK { + var errStr string + if len(response.Body) > 0 { + errStr = string(response.Body) + } else { + errStr = "no response body" + } + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("received status code: %v error: %s", response.StatusCode, errStr), + }} + } + + var br openrtb.BidResponse + err := json.Unmarshal(response.Body, &br) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + var errs []error + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + for i := range br.SeatBid { + for j := range br.SeatBid[i].Bid { + var ext avocetBidExt + if len(br.SeatBid[i].Bid[j].Ext) > 0 { + err := json.Unmarshal(br.SeatBid[i].Bid[j].Ext, &ext) + if err != nil { + errs = append(errs, err) + continue + } + } + tbid := &adapters.TypedBid{ + Bid: &br.SeatBid[i].Bid[j], + DealPriority: ext.Avocet.DealPriority, + } + tbid.BidType = getBidType(br.SeatBid[i].Bid[j], ext) + if tbid.BidType == openrtb_ext.BidTypeVideo { + tbid.BidVideo = &openrtb_ext.ExtBidPrebidVideo{ + Duration: ext.Avocet.Duration, + } + } + bidResponse.Bids = append(bidResponse.Bids, tbid) + } + } + return bidResponse, nil +} + +// getBidType returns the openrtb_ext.BidType for the provided bid. +func getBidType(bid openrtb.Bid, ext avocetBidExt) openrtb_ext.BidType { + if ext.Avocet.Duration != 0 { + return openrtb_ext.BidTypeVideo + } + switch bid.API { + case openrtb.APIFrameworkVPAID10, openrtb.APIFrameworkVPAID20: + return openrtb_ext.BidTypeVideo + default: + return openrtb_ext.BidTypeBanner + } +} + +// NewAvocetAdapter returns a new AvocetAdapter using the provided endpoint. +func NewAvocetAdapter(endpoint string) *AvocetAdapter { + return &AvocetAdapter{ + Endpoint: endpoint, + } +} diff --git a/adapters/avocet/avocet/exemplary/banner.json b/adapters/avocet/avocet/exemplary/banner.json new file mode 100644 index 00000000000..b5e308ea725 --- /dev/null +++ b/adapters/avocet/avocet/exemplary/banner.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + } + ], + "seat": "TEST_SEAT_ID" + } + ] + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + }, + "type": "banner" + } + ] +} diff --git a/adapters/avocet/avocet/exemplary/video.json b/adapters/avocet/avocet/exemplary/video.json new file mode 100644 index 00000000000..2398256b0dd --- /dev/null +++ b/adapters/avocet/avocet/exemplary/video.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "bidid": "a0eec3aa-f9f6-42fb-9aa4-f1b5656d4f42", + "id": "749d36d7-c993-455f-aefd-ffd8a7e3ccf", + "seatbid": [ + { + "bid": [ + { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": { + "avocet": { + "duration": 30 + } + } + } + ], + "seat": "TEST_SEAT_ID" + } + ] + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": { + "avocet": { + "duration": 30 + } + } + }, + "type": "video" + } + ] +} diff --git a/adapters/avocet/avocet_test.go b/adapters/avocet/avocet_test.go new file mode 100644 index 00000000000..ff2159bf406 --- /dev/null +++ b/adapters/avocet/avocet_test.go @@ -0,0 +1,301 @@ +package avocet + +import ( + "encoding/json" + "net/http" + "reflect" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "avocet", NewAvocetAdapter("https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013")) +} + +func TestAvocetAdapter_MakeRequests(t *testing.T) { + type fields struct { + Endpoint string + } + type args struct { + request *openrtb.BidRequest + reqInfo *adapters.ExtraRequestInfo + } + type reqData []*adapters.RequestData + tests := []struct { + name string + fields fields + args args + want []*adapters.RequestData + wantErrs []error + }{ + { + name: "return nil if zero imps", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + &openrtb.BidRequest{}, + nil, + }, + want: nil, + wantErrs: nil, + }, + { + name: "makes POST request with JSON content", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + &openrtb.BidRequest{Imp: []openrtb.Imp{{}}}, + nil, + }, + want: reqData{ + &adapters.RequestData{ + Method: http.MethodPost, + Uri: "https://bid.avct.cloud", + Body: []byte(`{"id":"","imp":[{"id":""}]}`), + Headers: map[string][]string{ + "Accept": {"application/json"}, + "Content-Type": {"application/json;charset=utf-8"}, + }, + }, + }, + wantErrs: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AvocetAdapter{ + Endpoint: tt.fields.Endpoint, + } + got, got1 := a.MakeRequests(tt.args.request, tt.args.reqInfo) + if len(got) != len(tt.want) { + t.Errorf("AvocetAdapter.MakeRequests() got %v requests, wanted %v requests", len(got), len(tt.want)) + } + if len(got) == len(tt.want) { + for i := range tt.want { + if !reflect.DeepEqual(got[i], tt.want[i]) { + t.Errorf("AvocetAdapter.MakeRequests() got = %v, want %v", got[i], tt.want[i]) + } + } + } + if !reflect.DeepEqual(got1, tt.wantErrs) { + t.Errorf("AvocetAdapter.MakeRequests() got1 = %v, want %v", got1, tt.wantErrs) + } + }) + } +} + +func TestAvocetAdapter_MakeBids(t *testing.T) { + type fields struct { + Endpoint string + } + type args struct { + internalRequest *openrtb.BidRequest + externalRequest *adapters.RequestData + response *adapters.ResponseData + } + tests := []struct { + name string + fields fields + args args + want *adapters.BidderResponse + errs []error + }{ + { + name: "204 No Content indicates no bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusNoContent}, + }, + want: nil, + errs: nil, + }, + { + name: "Non-200 return error", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusBadRequest, Body: []byte("message")}, + }, + want: nil, + errs: []error{&errortypes.BadServerResponse{Message: "received status code: 400 error: message"}}, + }, + { + name: "200 response containing banner bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusOK, Body: validBannerBidResponseBody}, + }, + want: &adapters.BidderResponse{ + Currency: "USD", + Bids: []*adapters.TypedBid{ + { + Bid: &validBannerBid, + BidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + errs: nil, + }, + { + name: "200 response containing video bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusOK, Body: validVideoBidResponseBody}, + }, + want: &adapters.BidderResponse{ + Currency: "USD", + Bids: []*adapters.TypedBid{ + { + Bid: &validVideoBid, + BidType: openrtb_ext.BidTypeVideo, + BidVideo: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 30, + }, + }, + }, + }, + errs: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AvocetAdapter{ + Endpoint: tt.fields.Endpoint, + } + got, got1 := a.MakeBids(tt.args.internalRequest, tt.args.externalRequest, tt.args.response) + if !reflect.DeepEqual(got, tt.want) { + gotb, _ := json.Marshal(got) + wantb, _ := json.Marshal(tt.want) + t.Errorf("AvocetAdapter.MakeBids() got = %s, want %s", string(gotb), string(wantb)) + } + if !reflect.DeepEqual(got1, tt.errs) { + t.Errorf("AvocetAdapter.MakeBids() got1 = %v, want %v", got1, tt.errs) + } + }) + } +} + +func Test_getBidType(t *testing.T) { + type args struct { + bid openrtb.Bid + ext avocetBidExt + } + tests := []struct { + name string + args args + want openrtb_ext.BidType + }{ + { + name: "VPAID 1.0", + args: args{openrtb.Bid{API: openrtb.APIFrameworkVPAID10}, avocetBidExt{}}, + want: openrtb_ext.BidTypeVideo, + }, + { + name: "VPAID 2.0", + args: args{openrtb.Bid{API: openrtb.APIFrameworkVPAID20}, avocetBidExt{}}, + want: openrtb_ext.BidTypeVideo, + }, + { + name: "other", + args: args{openrtb.Bid{}, avocetBidExt{}}, + want: openrtb_ext.BidTypeBanner, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getBidType(tt.args.bid, tt.args.ext); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getBidType() = %v, want %v", got, tt.want) + } + }) + } +} + +var validBannerBid = openrtb.Bid{ + AdM: "", + ADomain: []string{"avocet.io"}, + CID: "5b51e2d689654741306813a4", + CrID: "5b51e49634f2021f127ff7c9", + H: 250, + ID: "bc708396-9202-437b-b726-08b9864cb8b8", + ImpID: "test-imp-id", + IURL: "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + Language: "en", + Price: 15.64434783, + W: 300, +} + +var validBannerBidResponseBody = []byte(`{ + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + } + ], + "seat": "TEST_SEAT_ID" + } + ] +}`) + +var validVideoBid = openrtb.Bid{ + AdM: "Avocet", + ADomain: []string{"avocet.io"}, + CID: "5b51e2d689654741306813a4", + CrID: "5ec530e32d57fe1100f17d87", + H: 396, + ID: "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + ImpID: "dfp-ad--top-above-nav", + IURL: "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + Language: "en", + Price: 15.64434783, + W: 600, + Ext: []byte(`{"avocet":{"duration":30}}`), +} + +var validVideoBidResponseBody = []byte(`{ + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": {"avocet":{"duration":30}} + } + ], + "seat": "TEST_SEAT_ID" + } + ] +}`) diff --git a/adapters/avocet/usersync.go b/adapters/avocet/usersync.go new file mode 100644 index 00000000000..f1075ab3c52 --- /dev/null +++ b/adapters/avocet/usersync.go @@ -0,0 +1,12 @@ +package avocet + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewAvocetSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("avocet", 63, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go new file mode 100644 index 00000000000..8fba403f1b1 --- /dev/null +++ b/adapters/avocet/usersync_test.go @@ -0,0 +1,35 @@ +package avocet + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestAvocetSyncer(t *testing.T) { + syncURL := "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url=%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAvocetSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "ConsentString", + }, + CCPA: ccpa.Policy{ + Value: "PrivacyString", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://ads.avct.cloud/getuid?&gdpr=1&gdpr_consent=ConsentString&us_privacy=PrivacyString&url=%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D1%26gdpr_consent%3DConsentString%26uid%3D%7B%7BUUID%7D%7D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 63, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 0f470c6a611..01de9b1ab2e 100755 --- a/config/config.go +++ b/config/config.go @@ -567,6 +567,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAJA, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Daja%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25s") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAvocet, "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeintoo, "https://ib.beintoo.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeintoo%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -772,6 +773,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid") v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs v.SetDefault("adapters.appnexus.platform_id", "5") + v.SetDefault("adapters.avocet.disabled", true) v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display") v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um") diff --git a/docs/bidders/avocet.md b/docs/bidders/avocet.md new file mode 100644 index 00000000000..6aa67391af4 --- /dev/null +++ b/docs/bidders/avocet.md @@ -0,0 +1,5 @@ +# Avocet Bidder + +Please contact Avocet at info@avocet.io if you would like to get started selling inventory via the Avocet platform. + +**Note:** Avocet is disabled by default. Please enable it in the app config if you wish to use it. This can be done by setting `adapters.avocet.disabled` to `false` and by setting `adapters.avocet.endpoint` to a valid Avocet endpoint url. \ No newline at end of file diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 2ea8f7fb648..6e771236fb7 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -25,6 +25,7 @@ import ( "github.com/prebid/prebid-server/adapters/applogy" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" + "github.com/prebid/prebid-server/adapters/avocet" "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" @@ -106,6 +107,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAJA: aja.NewAJABidder(cfg.Adapters[string(openrtb_ext.BidderAJA)].Endpoint), openrtb_ext.BidderApplogy: applogy.NewApplogyBidder(cfg.Adapters[string(openrtb_ext.BidderApplogy)].Endpoint), openrtb_ext.BidderAppnexus: appnexus.NewAppNexusBidder(client, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].PlatformID), + openrtb_ext.BidderAvocet: avocet.NewAvocetAdapter(cfg.Adapters[string(openrtb_ext.BidderAvocet)].Endpoint), openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo), openrtb_ext.BidderBeintoo: beintoo.NewBeintooBidder(cfg.Adapters[string(openrtb_ext.BidderBeintoo)].Endpoint), openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 659c6616fea..416f36d135f 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -40,6 +40,7 @@ const ( BidderApplogy BidderName = "applogy" BidderAppnexus BidderName = "appnexus" BidderAdoppler BidderName = "adoppler" + BidderAvocet BidderName = "avocet" BidderBeachfront BidderName = "beachfront" BidderBeintoo BidderName = "beintoo" BidderBrightroll BidderName = "brightroll" @@ -118,6 +119,7 @@ var BidderMap = map[string]BidderName{ "applogy": BidderApplogy, "appnexus": BidderAppnexus, "adoppler": BidderAdoppler, + "avocet": BidderAvocet, "beachfront": BidderBeachfront, "beintoo": BidderBeintoo, "brightroll": BidderBrightroll, diff --git a/openrtb_ext/imp_avocet.go b/openrtb_ext/imp_avocet.go new file mode 100644 index 00000000000..7c9ca8c6eed --- /dev/null +++ b/openrtb_ext/imp_avocet.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpAvocet defines the contract for bidrequest.imp[i].ext.avocet +type ExtImpAvocet struct { + Placement string `json:"placement,omitempty"` + PlacementCode string `json:"placement_code,omitempty"` +} diff --git a/static/bidder-info/avocet.yaml b/static/bidder-info/avocet.yaml new file mode 100644 index 00000000000..ea98982d69c --- /dev/null +++ b/static/bidder-info/avocet.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "developers@avocet.io" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/avocet.json b/static/bidder-params/avocet.json new file mode 100644 index 00000000000..f27e5950f7c --- /dev/null +++ b/static/bidder-params/avocet.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Avocet Adapter Params", + "description": "A schema which validates params accepted by the Avocet adapter", + "type": "object", + "properties": { + "placement": { + "type": "string", + "description": "An Avocet placement ID" + }, + "placement_code": { + "type": "string", + "description": "An Avocet placement external code" + } + }, + "oneOf": [ + { + "required": ["placement"] + }, + { + "required": ["placement_code"] + } + ] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 751d2aabfbe..1beb9d586df 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/adapters/aja" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" + "github.com/prebid/prebid-server/adapters/avocet" "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" @@ -90,6 +91,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAJA, aja.NewAJASyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAppnexus, appnexus.NewAppnexusSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAvocet, avocet.NewAvocetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeachfront, beachfront.NewBeachfrontSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeintoo, beintoo.NewBeintooSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBrightroll, brightroll.NewBrightrollSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index c9ef382fc92..69751dd55f4 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -26,6 +26,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdvangelists): syncConfig, string(openrtb_ext.BidderAJA): syncConfig, string(openrtb_ext.BidderAppnexus): syncConfig, + string(openrtb_ext.BidderAvocet): syncConfig, string(openrtb_ext.BidderBeachfront): syncConfig, string(openrtb_ext.BidderBeintoo): syncConfig, string(openrtb_ext.BidderBrightroll): syncConfig, From a8feeca97c88cd6ca41483fdba02123f5d40b691 Mon Sep 17 00:00:00 2001 From: Marcin Muras <47107445+mmuras@users.noreply.github.com> Date: Thu, 18 Jun 2020 17:27:42 +0200 Subject: [PATCH 120/318] AdOcean adapter - Support for sizes defined in prebid configuration. (#1339) support for multiple sizes bump version to 1.1.0 --- adapters/adocean/adocean.go | 155 ++++++++++++---- .../exemplary/multi-banner-impression.json | 5 +- .../exemplary/single-banner-impression.json | 2 +- .../supplemental/bad-response.json | 2 +- .../supplemental/encode-error.json | 2 +- .../supplemental/network-error.json | 2 +- .../adoceantest/supplemental/no-bid.json | 4 +- .../adoceantest/supplemental/no-sizes.json | 168 ++++++++++++++++++ .../supplemental/requests-merge.json | 4 +- 9 files changed, 298 insertions(+), 46 deletions(-) create mode 100644 adapters/adocean/adoceantest/supplemental/no-sizes.json diff --git a/adapters/adocean/adocean.go b/adapters/adocean/adocean.go index 514aeb38424..8740712b6a0 100644 --- a/adapters/adocean/adocean.go +++ b/adapters/adocean/adocean.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "regexp" + "sort" "strconv" "strings" "text/template" @@ -19,7 +20,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) -const adapterVersion = "1.0.0" +const adapterVersion = "1.1.0" const maxUriLength = 8000 const measurementCode = ` ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }, { + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-two", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }, { + "currency": "EUR", + "bids": [{ + "bid": { + "id": "adoceanmyaowafpdwlrks", + "impid": "ao-test-three", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/adocean/adoceantest/supplemental/requests-merge.json b/adapters/adocean/adoceantest/supplemental/requests-merge.json index 9b5eb39aee2..e0736ec918f 100644 --- a/adapters/adocean/adoceantest/supplemental/requests-merge.json +++ b/adapters/adocean/adoceantest/supplemental/requests-merge.json @@ -83,7 +83,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&aid=adoceanmyaowafpdwlrks%3Aao-test-two&aosspsizes=myaowafpdwlrks~300x250-myaozpniqismex~300x250&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.1.0" }, "mockResponse": { "status": 200, @@ -117,7 +117,7 @@ } }, { "expectedRequest": { - "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0" + "uri": "https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaowafpdwlrks%3Aao-test-three&aosspsizes=myaowafpdwlrks~300x250&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.1.0" }, "mockResponse": { "status": 200, From 9c79ee485422e4e8383df8e288e5e20b95ec6841 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Thu, 18 Jun 2020 11:59:13 -0400 Subject: [PATCH 121/318] =?UTF-8?q?Log=20account=20id=20and=20all=20bidder?= =?UTF-8?q?=20names=20when=20recovering=20from=20OpenRTB=20auction=20bidde?= =?UTF-8?q?r=E2=80=A6=20(#1358)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exchange/exchange.go | 19 ++++++++++++++++--- exchange/exchange_test.go | 10 +++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index 84ae35d644c..d7eab0f4475 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -303,7 +303,7 @@ func (e *exchange) getAllBids(ctx context.Context, cleanRequests map[openrtb_ext for bidderName, req := range cleanRequests { // Here we actually call the adapters and collect the bids. coreBidder := resolveBidder(string(bidderName), aliases) - bidderRunner := e.recoverSafely(func(aName openrtb_ext.BidderName, coreBidder openrtb_ext.BidderName, request *openrtb.BidRequest, bidlabels *pbsmetrics.AdapterLabels, conversions currencies.Conversions) { + bidderRunner := e.recoverSafely(cleanRequests, func(aName openrtb_ext.BidderName, coreBidder openrtb_ext.BidderName, request *openrtb.BidRequest, bidlabels *pbsmetrics.AdapterLabels, conversions currencies.Conversions) { // Passing in aName so a doesn't change out from under the go routine if bidlabels.Adapter == "" { glog.Errorf("Exchange: bidlables for %s (%s) missing adapter string", aName, coreBidder) @@ -373,11 +373,24 @@ func (e *exchange) getAllBids(ctx context.Context, cleanRequests map[openrtb_ext return adapterBids, adapterExtra, bidsFound } -func (e *exchange) recoverSafely(inner func(openrtb_ext.BidderName, openrtb_ext.BidderName, *openrtb.BidRequest, *pbsmetrics.AdapterLabels, currencies.Conversions), chBids chan *bidResponseWrapper) func(openrtb_ext.BidderName, openrtb_ext.BidderName, *openrtb.BidRequest, *pbsmetrics.AdapterLabels, currencies.Conversions) { +func (e *exchange) recoverSafely(cleanRequests map[openrtb_ext.BidderName]*openrtb.BidRequest, inner func(openrtb_ext.BidderName, openrtb_ext.BidderName, *openrtb.BidRequest, *pbsmetrics.AdapterLabels, currencies.Conversions), chBids chan *bidResponseWrapper) func(openrtb_ext.BidderName, openrtb_ext.BidderName, *openrtb.BidRequest, *pbsmetrics.AdapterLabels, currencies.Conversions) { return func(aName openrtb_ext.BidderName, coreBidder openrtb_ext.BidderName, request *openrtb.BidRequest, bidlabels *pbsmetrics.AdapterLabels, conversions currencies.Conversions) { defer func() { if r := recover(); r != nil { - glog.Errorf("OpenRTB auction recovered panic from Bidder %s: %v. Stack trace is: %v", coreBidder, r, string(debug.Stack())) + + allBidders := "" + sb := strings.Builder{} + for k := range cleanRequests { + sb.WriteString(string(k)) + sb.WriteString(",") + } + if sb.Len() > 0 { + allBidders = sb.String()[:sb.Len()-1] + } + + glog.Errorf("OpenRTB auction recovered panic from Bidder %s: %v. "+ + "Account id: %s, All Bidders: %s, Stack trace is: %v", + coreBidder, r, bidlabels.PubID, allBidders, string(debug.Stack())) e.me.RecordAdapterPanic(*bidlabels) // Let the master request know that there is no data here brw := new(bidResponseWrapper) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 4f329962a53..93cb60fb5af 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -582,7 +582,15 @@ func TestPanicRecovery(t *testing.T) { panicker := func(aName openrtb_ext.BidderName, coreBidder openrtb_ext.BidderName, request *openrtb.BidRequest, bidlabels *pbsmetrics.AdapterLabels, conversions currencies.Conversions) { panic("panic!") } - recovered := e.recoverSafely(panicker, chBids) + cleanReqs := map[openrtb_ext.BidderName]*openrtb.BidRequest{ + "bidder1": { + ID: "b-1", + }, + "bidder2": { + ID: "b-2", + }, + } + recovered := e.recoverSafely(cleanReqs, panicker, chBids) apnLabels := pbsmetrics.AdapterLabels{ Source: pbsmetrics.DemandWeb, RType: pbsmetrics.ReqTypeORTB2Web, From 5f39344c43d56331c69a855c979483a5e3d84086 Mon Sep 17 00:00:00 2001 From: tadam75 Date: Sat, 20 Jun 2020 19:22:25 +0200 Subject: [PATCH 122/318] Adding Smartadserver adapter (#1346) Co-authored-by: tadam --- adapters/smartadserver/params_test.go | 61 ++++++ adapters/smartadserver/smartadserver.go | 179 ++++++++++++++++++ adapters/smartadserver/smartadserver_test.go | 11 ++ .../exemplary/multi-banner.json | 175 +++++++++++++++++ .../exemplary/simple-banner.json | 94 +++++++++ .../exemplary/simple-video.json | 100 ++++++++++ .../smartadservertest/params/race/banner.json | 7 + .../smartadservertest/params/race/video.json | 7 + .../request-no-bidder-object.json | 21 ++ .../supplemental/request-no-ext-object.json | 19 ++ .../supplemental/request-no-imp.json | 13 ++ .../supplemental/request-site-recreated.json | 99 ++++++++++ .../response-200-without-body.json | 62 ++++++ .../supplemental/response-204.json | 56 ++++++ .../supplemental/response-400.json | 62 ++++++ .../supplemental/response-500.json | 62 ++++++ adapters/smartadserver/usersync.go | 12 ++ adapters/smartadserver/usersync_test.go | 35 ++++ config/config.go | 2 + docs/bidders/smartAdserver.md | 59 ++++++ exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_smartadserver.go | 9 + static/bidder-info/smartadserver.yaml | 11 ++ static/bidder-params/smartadserver.json | 35 ++++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 27 files changed, 1198 insertions(+) create mode 100644 adapters/smartadserver/params_test.go create mode 100644 adapters/smartadserver/smartadserver.go create mode 100644 adapters/smartadserver/smartadserver_test.go create mode 100644 adapters/smartadserver/smartadservertest/exemplary/multi-banner.json create mode 100644 adapters/smartadserver/smartadservertest/exemplary/simple-banner.json create mode 100644 adapters/smartadserver/smartadservertest/exemplary/simple-video.json create mode 100644 adapters/smartadserver/smartadservertest/params/race/banner.json create mode 100644 adapters/smartadserver/smartadservertest/params/race/video.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/request-no-bidder-object.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/request-no-ext-object.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/request-no-imp.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/request-site-recreated.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/response-200-without-body.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/response-204.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/response-400.json create mode 100644 adapters/smartadserver/smartadservertest/supplemental/response-500.json create mode 100644 adapters/smartadserver/usersync.go create mode 100644 adapters/smartadserver/usersync_test.go create mode 100644 docs/bidders/smartAdserver.md create mode 100644 openrtb_ext/imp_smartadserver.go create mode 100644 static/bidder-info/smartadserver.yaml create mode 100644 static/bidder-params/smartadserver.json diff --git a/adapters/smartadserver/params_test.go b/adapters/smartadserver/params_test.go new file mode 100644 index 00000000000..6e45bb1d046 --- /dev/null +++ b/adapters/smartadserver/params_test.go @@ -0,0 +1,61 @@ +package smartadserver + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/smartadserver.json +// +// These also validate the format of the external API: request.imp[i].ext.smartadserver + +// TestValidParams makes sure that the smartadserver schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderSmartadserver, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected smartadserver params: %s \n Error: %s", validParam, err) + } + } +} + +// TestInvalidParams makes sure that the smartadserver schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderSmartadserver, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"networkId":73}`, + `{"networkId":73,"siteId":1,"pageId":2,"formatId":3}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"networkId":"73"}`, + `{"networkId":"73","siteId":"1","pageId":"2","formatId":"3"}`, + `{"siteId":1,"pageId":2,"formatId":3}`, + `{"networkId":73,"pageId":2,"formatId":3}`, + `{"networkId":73,"siteId":1,"formatId":3}`, + `{"networkId":73,"siteId":1,"pageId":2}`, +} diff --git a/adapters/smartadserver/smartadserver.go b/adapters/smartadserver/smartadserver.go new file mode 100644 index 00000000000..c35b749c51c --- /dev/null +++ b/adapters/smartadserver/smartadserver.go @@ -0,0 +1,179 @@ +package smartadserver + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strconv" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type SmartAdserverAdapter struct { + host string +} + +func NewSmartadserverBidder(host string) *SmartAdserverAdapter { + return &SmartAdserverAdapter{ + host: host, + } +} + +// MakeRequests makes the HTTP requests which should be made to fetch bids. +func (a *SmartAdserverAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impression in the bid request", + }} + } + + var adapterRequests []*adapters.RequestData + var errs []error + + // We copy the original request. + smartRequest := *request + + // We create or copy the Site object. + if smartRequest.Site == nil { + smartRequest.Site = &openrtb.Site{} + } else { + site := *smartRequest.Site + smartRequest.Site = &site + } + + // We create or copy the Publisher object. + if smartRequest.Site.Publisher == nil { + smartRequest.Site.Publisher = &openrtb.Publisher{} + } else { + publisher := *smartRequest.Site.Publisher + smartRequest.Site.Publisher = &publisher + } + + // We send one serialized "smartRequest" per impression of the original request. + for _, imp := range request.Imp { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: "Error parsing bidderExt object", + }) + continue + } + + var smartadserverExt openrtb_ext.ExtImpSmartadserver + if err := json.Unmarshal(bidderExt.Bidder, &smartadserverExt); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: "Error parsing smartadserverExt parameters", + }) + continue + } + + // Adding publisher id. + smartRequest.Site.Publisher.ID = strconv.Itoa(smartadserverExt.NetworkID) + + // We send one request for each impression. + smartRequest.Imp = []openrtb.Imp{imp} + + var errMarshal error + if imp.Ext, errMarshal = json.Marshal(smartadserverExt); errMarshal != nil { + errs = append(errs, &errortypes.BadInput{ + Message: errMarshal.Error(), + }) + continue + } + + reqJSON, err := json.Marshal(smartRequest) + if err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: "Error parsing reqJSON object", + }) + continue + } + + url, err := a.BuildEndpointURL(&smartadserverExt) + if url == "" { + errs = append(errs, err) + continue + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + adapterRequests = append(adapterRequests, &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: reqJSON, + Headers: headers, + }) + } + return adapterRequests, errs +} + +// MakeBids unpacks the server's response into Bids. +func (a *SmartAdserverAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Unexpected status code: " + strconv.Itoa(response.StatusCode) + ". Run with request.debug = 1 for more info", + }} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp), + }) + + } + } + return bidResponse, []error{} +} + +// BuildEndpointURL : Builds endpoint url +func (a *SmartAdserverAdapter) BuildEndpointURL(params *openrtb_ext.ExtImpSmartadserver) (string, error) { + uri, err := url.Parse(a.host) + if err != nil || uri.Scheme == "" || uri.Host == "" { + return "", &errortypes.BadInput{ + Message: "Malformed URL: " + a.host + ".", + } + } + + uri.Path = path.Join(uri.Path, "api/bid") + uri.RawQuery = "callerId=5" + + return uri.String(), nil +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + for _, imp := range imps { + if imp.ID == impID { + if imp.Video != nil { + return openrtb_ext.BidTypeVideo + } + return openrtb_ext.BidTypeBanner + } + } + return openrtb_ext.BidTypeBanner +} diff --git a/adapters/smartadserver/smartadserver_test.go b/adapters/smartadserver/smartadserver_test.go new file mode 100644 index 00000000000..7e4cff678cc --- /dev/null +++ b/adapters/smartadserver/smartadserver_test.go @@ -0,0 +1,11 @@ +package smartadserver + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "smartadservertest", NewSmartadserverBidder("https://ssb.smartadserver.com")) +} diff --git a/adapters/smartadserver/smartadservertest/exemplary/multi-banner.json b/adapters/smartadserver/smartadservertest/exemplary/multi-banner.json new file mode 100644 index 00000000000..b7cf27c37e2 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/exemplary/multi-banner.json @@ -0,0 +1,175 @@ +{ + "mockBidRequest": { + "id": "test-request-multi-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "format": [{"w": 300, "h": 150}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 4, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-multi-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-multi-id", + "seatbid": [ + { + "seat": "smartadserver", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-1", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "cur": "USD" + } + } + }, + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-multi-id", + "imp": [ + { + "id": "test-imp-id-2", + "banner": { + "format": [{"w": 300, "h": 150}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 4, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-multi-id", + "seatbid": [ + { + "seat": "smartadserver", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 0.800000, + "adm": "some-test-ad", + "crid": "crid_11", + "h": 150, + "w": 300 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 0.8, + "adm": "some-test-ad", + "crid": "crid_11", + "w": 300, + "h": 150 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/exemplary/simple-banner.json b/adapters/smartadserver/smartadservertest/exemplary/simple-banner.json new file mode 100644 index 00000000000..e8faab141cd --- /dev/null +++ b/adapters/smartadserver/smartadservertest/exemplary/simple-banner.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "smartadserver", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/exemplary/simple-video.json b/adapters/smartadserver/smartadservertest/exemplary/simple-video.json new file mode 100644 index 00000000000..86f9361a807 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/exemplary/simple-video.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "protocols": [1], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "protocols": [1], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-video", + "seatbid": [ + { + "seat": "smartadserver", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-video", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 576, + "w": 1024 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-video", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 1024, + "h": 576 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/params/race/banner.json b/adapters/smartadserver/smartadservertest/params/race/banner.json new file mode 100644 index 00000000000..b34088307d4 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/params/race/banner.json @@ -0,0 +1,7 @@ +{ + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + \ No newline at end of file diff --git a/adapters/smartadserver/smartadservertest/params/race/video.json b/adapters/smartadserver/smartadservertest/params/race/video.json new file mode 100644 index 00000000000..b34088307d4 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/params/race/video.json @@ -0,0 +1,7 @@ +{ + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + \ No newline at end of file diff --git a/adapters/smartadserver/smartadservertest/supplemental/request-no-bidder-object.json b/adapters/smartadserver/smartadservertest/supplemental/request-no-bidder-object.json new file mode 100644 index 00000000000..48664a66073 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/request-no-bidder-object.json @@ -0,0 +1,21 @@ +{ + "mockBidRequest": { + "id": "test-no-bidder", + "imp": [ + { + "id": "test-imp-id-no-bidder", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Error parsing smartadserverExt parameters", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/request-no-ext-object.json b/adapters/smartadserver/smartadservertest/supplemental/request-no-ext-object.json new file mode 100644 index 00000000000..d6637d0ebc3 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/request-no-ext-object.json @@ -0,0 +1,19 @@ +{ + "mockBidRequest": { + "id": "test-no-ext", + "imp": [ + { + "id": "test-imp-id-no-ext", + "banner": { + "format": [{"w": 728, "h": 90}] + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Error parsing bidderExt object", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/request-no-imp.json b/adapters/smartadserver/smartadservertest/supplemental/request-no-imp.json new file mode 100644 index 00000000000..50e00a8a969 --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/request-no-imp.json @@ -0,0 +1,13 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the bid request", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/request-site-recreated.json b/adapters/smartadserver/smartadservertest/supplemental/request-site-recreated.json new file mode 100644 index 00000000000..4a402674abf --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/request-site-recreated.json @@ -0,0 +1,99 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "1" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "smartadserver", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/response-200-without-body.json b/adapters/smartadserver/smartadservertest/supplemental/response-200-without-body.json new file mode 100644 index 00000000000..3e27569491c --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/response-200-without-body.json @@ -0,0 +1,62 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 200 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/response-204.json b/adapters/smartadserver/smartadservertest/supplemental/response-204.json new file mode 100644 index 00000000000..32aa2642f0a --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/response-204.json @@ -0,0 +1,56 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 204 + } + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/response-400.json b/adapters/smartadserver/smartadservertest/supplemental/response-400.json new file mode 100644 index 00000000000..b7d5a95475d --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/response-400.json @@ -0,0 +1,62 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/smartadservertest/supplemental/response-500.json b/adapters/smartadserver/smartadservertest/supplemental/response-500.json new file mode 100644 index 00000000000..727e8f0843b --- /dev/null +++ b/adapters/smartadserver/smartadservertest/supplemental/response-500.json @@ -0,0 +1,62 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ssb.smartadserver.com/api/bid?callerId=5", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": 1, + "pageId": 2, + "formatId": 3, + "networkId": 73 + } + } + } + ], + "site": { + "publisher": { + "id": "73" + } + } + } + }, + "mockResponse": { + "status": 500 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/smartadserver/usersync.go b/adapters/smartadserver/usersync.go new file mode 100644 index 00000000000..95b305ff227 --- /dev/null +++ b/adapters/smartadserver/usersync.go @@ -0,0 +1,12 @@ +package smartadserver + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewSmartadserverSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("smartadserver", 45, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/smartadserver/usersync_test.go b/adapters/smartadserver/usersync_test.go new file mode 100644 index 00000000000..e279b49e017 --- /dev/null +++ b/adapters/smartadserver/usersync_test.go @@ -0,0 +1,35 @@ +package smartadserver + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestSmartadserverSyncer(t *testing.T) { + syncURL := "//ssbsync.smartadserver.com/getuid?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url=localhost%2Fsetuid%3Fbidder%3Dsmartadserver%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bsas_uid%5D%22" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewSmartadserverSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "COyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA", + }, + CCPA: ccpa.Policy{ + Value: "1YNN", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "//ssbsync.smartadserver.com/getuid?gdpr=1&gdpr_consent=COyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA&us_privacy=1YNN&url=localhost%2Fsetuid%3Fbidder%3Dsmartadserver%26gdpr%3D1%26gdpr_consent%3DCOyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA%26uid%3D%5Bsas_uid%5D%22", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 45, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 01de9b1ab2e..c50118e2008 100755 --- a/config/config.go +++ b/config/config.go @@ -600,6 +600,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderRTBHouse doesn't have a good default. // openrtb_ext.BidderRubicon doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSharethrough, "https://match.sharethrough.com/FGMrCMMc/v1?redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsharethrough%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSmartadserver, "https://ssbsync.smartadserver.com/api/sync?callerId=5&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsmartadserver%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bssb_sync_pid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSmartRTB, "https://market-global.smrtb.com/sync/all?nid=smartrtb&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&rr="+url.QueryEscape(externalURL)+"%252Fsetuid%253Fbidder%253Dsmartrtb%2526gdpr%253D{{.GDPR}}%2526gdpr_consent%253D{{.GDPRConsent}}%2526uid%253D%257BXID%257D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSomoaudience, "https://publisher-east.mobileadtrading.com/usersync?ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSonobi, "https://sync.go.sonobi.com/us.gif?loc="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D{{.GDPR}}%26gdpr%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D") @@ -810,6 +811,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rtbhouse.endpoint", "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids") v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json") v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") + v.SetDefault("adapters.smartadserver.endpoint", "https://ssb.smartadserver.com") v.SetDefault("adapters.smartrtb.endpoint", "http://market-east.smrtb.com/json/publisher/rtb?pubid={{.PublisherID}}") v.SetDefault("adapters.somoaudience.endpoint", "http://publisher-east.mobileadtrading.com/rtb/bid") v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af") diff --git a/docs/bidders/smartAdserver.md b/docs/bidders/smartAdserver.md new file mode 100644 index 00000000000..4d2663f8a3b --- /dev/null +++ b/docs/bidders/smartAdserver.md @@ -0,0 +1,59 @@ +# Smart Adserver Bidder + +## Parameters +The `ext.smartadserver` object of impression bid requests supports the following parameters : +- "networkId" - Required. The network identifier you have been provided with. +- "siteId" - Optional. The site identifier from your campaign configuration. +- "pageId" - Optional. The page identifier from your campaign configuration. +- "formatId" - Optional. The format identifier from your campaign configuration. + +The network identifier is provided by your Account Manager. +**Note:** The site, page and format identifiers have to all be provided or all empty. + +## Examples + +Without site/page/format : +``` + "imp": [{ + "id": "some-impression-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "smartadserver": { + "networkId": 73 + } + } + }] +``` + +With site/page/format : + +``` + "imp": [{ + "id": "some-impression-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "smartadserver": { + "networkId": 73 + "siteId": 1, + "pageId": 2, + "formatId": 3 + } + } + }] +``` \ No newline at end of file diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 6e771236fb7..44054df06fd 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -62,6 +62,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/smartadserver" "github.com/prebid/prebid-server/adapters/smartrtb" "github.com/prebid/prebid-server/adapters/somoaudience" "github.com/prebid/prebid-server/adapters/sonobi" @@ -150,6 +151,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker), openrtb_ext.BidderSharethrough: sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint), + openrtb_ext.BidderSmartadserver: smartadserver.NewSmartadserverBidder(cfg.Adapters[string(openrtb_ext.BidderSmartadserver)].Endpoint), openrtb_ext.BidderSmartRTB: smartrtb.NewSmartRTBBidder(cfg.Adapters[string(openrtb_ext.BidderSmartRTB)].Endpoint), openrtb_ext.BidderSomoaudience: somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint), openrtb_ext.BidderSonobi: sonobi.NewSonobiBidder(client, cfg.Adapters[string(openrtb_ext.BidderSonobi)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 416f36d135f..1f9cffb9938 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -78,6 +78,7 @@ const ( BidderRTBHouse BidderName = "rtbhouse" BidderRubicon BidderName = "rubicon" BidderSharethrough BidderName = "sharethrough" + BidderSmartadserver BidderName = "smartadserver" BidderSmartRTB BidderName = "smartrtb" BidderSomoaudience BidderName = "somoaudience" BidderSonobi BidderName = "sonobi" @@ -157,6 +158,7 @@ var BidderMap = map[string]BidderName{ "rtbhouse": BidderRTBHouse, "rubicon": BidderRubicon, "sharethrough": BidderSharethrough, + "smartadserver": BidderSmartadserver, "smartrtb": BidderSmartRTB, "somoaudience": BidderSomoaudience, "sonobi": BidderSonobi, diff --git a/openrtb_ext/imp_smartadserver.go b/openrtb_ext/imp_smartadserver.go new file mode 100644 index 00000000000..d542e0ffd27 --- /dev/null +++ b/openrtb_ext/imp_smartadserver.go @@ -0,0 +1,9 @@ +package openrtb_ext + +// ExtImpSmartadserver defines the contract for bidrequest.imp[i].ext.smartadserver +type ExtImpSmartadserver struct { + SiteID int `json:"siteId"` + PageID int `json:"pageId"` + FormatID int `json:"formatId"` + NetworkID int `json:"networkId"` +} diff --git a/static/bidder-info/smartadserver.yaml b/static/bidder-info/smartadserver.yaml new file mode 100644 index 00000000000..626b7dac00d --- /dev/null +++ b/static/bidder-info/smartadserver.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "support@smartadserver.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/smartadserver.json b/static/bidder-params/smartadserver.json new file mode 100644 index 00000000000..b76a3bd6ac9 --- /dev/null +++ b/static/bidder-params/smartadserver.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Smartadserver Adapter Params", + "description": "A schema which validates params accepted by the Smartadserver adapter", + + "type": "object", + "properties": { + "siteId": { + "type": "integer", + "description": "The site id.", + "minimum": 1 + }, + "pageId": { + "type": "integer", + "description": "The page id.", + "minimum": 1 + }, + "formatId": { + "type": "integer", + "description": "The format id.", + "minimum": 1 + }, + "networkId": { + "type": "integer", + "description": "The network id.", + "minimum": 1 + } + }, + "dependencies": { + "siteId": { "required": ["pageId", "formatId"] }, + "pageId": { "required": ["siteId", "formatId"] }, + "formatId": { "required": ["siteId", "pageId"] } + }, + "required": ["networkId"] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 1beb9d586df..5657c8b7010 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -50,6 +50,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/smartadserver" "github.com/prebid/prebid-server/adapters/smartrtb" "github.com/prebid/prebid-server/adapters/somoaudience" "github.com/prebid/prebid-server/adapters/sonobi" @@ -127,6 +128,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderSomoaudience, somoaudience.NewSomoaudienceSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSonobi, sonobi.NewSonobiSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderSmartadserver, smartadserver.NewSmartadserverSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSmartRTB, smartrtb.NewSmartRTBSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTelaria, telaria.NewTelariaSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 69751dd55f4..363cd491648 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -62,6 +62,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderSomoaudience): syncConfig, string(openrtb_ext.BidderSonobi): syncConfig, string(openrtb_ext.BidderSovrn): syncConfig, + string(openrtb_ext.BidderSmartadserver): syncConfig, string(openrtb_ext.BidderSmartRTB): syncConfig, string(openrtb_ext.BidderSynacormedia): syncConfig, string(openrtb_ext.BidderTelaria): syncConfig, From 379492dced7d01da04c4762cb2d38ad9d02a37e4 Mon Sep 17 00:00:00 2001 From: Telaria Engineering <36203956+telariaEng@users.noreply.github.com> Date: Sat, 20 Jun 2020 13:26:09 -0700 Subject: [PATCH 123/318] Added additional Ext Param (#1357) Co-authored-by: Vinay Prasad --- adapters/telaria/telaria.go | 20 +- .../telariatest/exemplary/video-app.json | 226 ++++++++++-------- .../telariatest/exemplary/video-web.json | 224 +++++++++-------- openrtb_ext/imp_telaria.go | 7 +- 4 files changed, 277 insertions(+), 200 deletions(-) diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go index 294d0d100a9..6bed043152e 100644 --- a/adapters/telaria/telaria.go +++ b/adapters/telaria/telaria.go @@ -23,6 +23,10 @@ type ImpressionExtOut struct { OriginalPublisherID string `json:"originalPublisherid"` } +type telariaBidExt struct { + Extra json.RawMessage `json:"extra,omitempty"` +} + // used for cookies and such func (a *TelariaAdapter) Name() string { return "telaria" @@ -186,15 +190,17 @@ func (a *TelariaAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo *ad originalPublisherID := a.FetchOriginalPublisherID(&request) var errors []error + var telariaImpExt *openrtb_ext.ExtImpTelaria + var err error for i, imp := range request.Imp { // fetch adCode & seatCode from Imp[i].Ext - telariaExt, err := a.FetchTelariaExtImpParams(&imp) + telariaImpExt, err = a.FetchTelariaExtImpParams(&imp) if err != nil { errors = append(errors, err) break } - seatCode = telariaExt.SeatCode + seatCode = telariaImpExt.SeatCode // move the original tagId and the original publisher.id into the Imp[i].Ext object request.Imp[i].Ext, err = json.Marshal(&ImpressionExtOut{request.Imp[i].TagID, originalPublisherID}) @@ -204,7 +210,15 @@ func (a *TelariaAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo *ad } // Swap the tagID with adCode - request.Imp[i].TagID = telariaExt.AdCode + request.Imp[i].TagID = telariaImpExt.AdCode + } + + // Add the Extra from Imp to the top level Ext + if telariaImpExt != nil && telariaImpExt.Extra != nil { + request.Ext, err = json.Marshal(&telariaBidExt{Extra: telariaImpExt.Extra}) + if err != nil { + errors = append(errors, err) + } } if len(errors) > 0 { diff --git a/adapters/telaria/telariatest/exemplary/video-app.json b/adapters/telaria/telariatest/exemplary/video-app.json index fa755cc93d3..6450509c8e1 100644 --- a/adapters/telaria/telariatest/exemplary/video-app.json +++ b/adapters/telaria/telariatest/exemplary/video-app.json @@ -39,118 +39,148 @@ "ext": { "bidder": { "adCode": "my-adcode", - "seatCode": "my-seatcode" + "seatCode": "my-seatcode", + "extra": { + "custom": "1234" + } } } } ] }, - "httpCalls": [{ - "expectedRequest": { - "headers": { - "Content-Type": ["application/json;charset=utf-8"], - "Accept": ["application/json"], - "X-Openrtb-Version": ["2.5"], - "User-Agent": ["test-user-agent"], - "X-Forwarded-For": ["123.123.123.123"], - "Accept-Language": ["en"], - "Dnt": ["0"] - }, - "uri": "https://ads.tremorhub.com/ad/rtb/prebid", - "body": { - "id": "some-request-id", - "device": { - "ua": "test-user-agent", - "ip": "123.123.123.123", - "language": "en", - "dnt": 0 + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Accept-Language": [ + "en" + ], + "Dnt": [ + "0" + ] }, - "imp": [ - { - "id": "some-impression-id", - "video": { - "mimes": [ - "video/mp4" - ], - "minduration": 120, - "maxduration": 150, - "w": 640, - "h": 480 - }, - "tagid": "my-adcode", - "ext": { - "originalTagid": "ogTAGID", - "originalPublisherid": "123456789" + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "ext": { + "extra": { + "custom": "1234" + } + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "my-adcode", + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" + } } - } - ], - "app": { - "id": "123456789", - "name": "Awesome App", - "bundle": "com.app.awesome", - "domain": "awesomeapp.com", - "cat": [ - "IAB22-1" ], - "publisher": { - "id": "my-seatcode" - } - }, - "user": { - "buyeruid": "awesome-user" - }, - "tmax": 1000 - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "awesome-resp-id", - "seatbid": [{ - "bid": [{ - "id": "a3ae1b4e2fc24a4fb45540082e98e161", - "impid": "1", - "price": 3.5, - "adm": "asesome-markup", - "adomain": [ - "awesome.com" + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" ], - "crid": "20", - "w": 1280, - "h": 720, - "ext": { - "prebid": { - "type": "video" - } + "publisher": { + "id": "my-seatcode" } - }], - "seat": "telaria" - }], - "cur": "USD", - "ext": { - "responsetimemillis": { - "telaria": 154 }, - "tmaxrequest": 1000 + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "telaria" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "telaria": 154 + }, + "tmaxrequest": 1000 + } } } } - }], - "expectedBids": [{ - "id": "a3ae1b4e2fc24a4fb45540082e98e161", - "impid": "1", - "price": 3.5, - "adm": "awesome-markup", - "adomain": [ - "awesome.com" - ], - "crid": "20", - "w": 1280, - "h": 720, - "ext": { - "prebid": { - "type": "video" + ], + "expectedBids": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } } } - }] + ] } diff --git a/adapters/telaria/telariatest/exemplary/video-web.json b/adapters/telaria/telariatest/exemplary/video-web.json index c671d2f5f30..f3a3a56928c 100644 --- a/adapters/telaria/telariatest/exemplary/video-web.json +++ b/adapters/telaria/telariatest/exemplary/video-web.json @@ -1,4 +1,3 @@ - { "mockBidRequest": { "id": "some-request-id", @@ -23,7 +22,9 @@ "id": "some-impression-id", "tagid": "ogTAGID", "video": { - "mimes": ["video/mp4"], + "mimes": [ + "video/mp4" + ], "w": 640, "h": 480, "minduration": 120, @@ -32,113 +33,142 @@ "ext": { "bidder": { "adCode": "my-adcode", - "seatCode": "my-seatcode" + "seatCode": "my-seatcode", + "extra": { + "custom": "1234" + } } } } ] }, - - "httpCalls": [{ - "expectedRequest": { - "headers": { - "Content-Type": ["application/json;charset=utf-8"], - "Accept": ["application/json"], - "X-Openrtb-Version": ["2.5"], - "User-Agent": ["test-user-agent"], - "X-Forwarded-For": ["123.123.123.123"], - "Accept-Language": ["en"], - "Dnt": ["0"] - }, - "uri": "https://ads.tremorhub.com/ad/rtb/prebid", - "body": { - "id": "some-request-id", - "device": { - "ua": "test-user-agent", - "ip": "123.123.123.123", - "language": "en", - "dnt": 0 - }, - "imp": [ - { - "id": "some-impression-id", - "tagid": "my-adcode", - "video": { - "mimes": [ - "video/mp4" - ], - "minduration": 120, - "maxduration": 150, - "w": 640, - "h": 480 - }, - "ext": { - "originalTagid": "ogTAGID", - "originalPublisherid": "123456789" - } - } - ], - "site": { - "page": "test.com", - "publisher": { - "id": "my-seatcode" - } - }, - "user": { - "buyeruid": "awesome-user" + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Accept-Language": [ + "en" + ], + "Dnt": [ + "0" + ] }, - "tmax": 1000 - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "awesome-resp-id", - "seatbid": [{ - "bid": [{ - "id": "a3ae1b4e2fc24a4fb45540082e98e161", - "impid": "1", - "price": 3.5, - "adm": "asesome-markup", - "adomain": [ - "awesome.com" - ], - "crid": "20", - "w": 1280, - "h": 720, - "ext": { - "prebid": { - "type": "video" + "uri": "https://ads.tremorhub.com/ad/rtb/prebid", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "originalTagid": "ogTAGID", + "originalPublisherid": "123456789" } } - }], - "seat": "telaria" - }], - "cur": "USD", - "ext": { - "responsetimemillis": { - "telaria": 154 + ], + "site": { + "page": "test.com", + "publisher": { + "id": "my-seatcode" + } + }, + "user": { + "buyeruid": "awesome-user" }, - "tmaxrequest": 1000 + "tmax": 1000, + "ext": { + "extra": { + "custom": "1234" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "telaria" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "telaria": 154 + }, + "tmaxrequest": 1000 + } } } } - }], - "expectedBids": [{ - "id": "a3ae1b4e2fc24a4fb45540082e98e161", - "impid": "1", - "price": 3.5, - "adm": "asesome-markup", - "adomain": [ - "awesome.com" - ], - "crid": "20", - "w": 1280, - "h": 720, - "ext": { - "prebid": { - "type": "video" + ], + "expectedBids": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } } } - }] + ] } diff --git a/openrtb_ext/imp_telaria.go b/openrtb_ext/imp_telaria.go index 8ea371a8ad0..19a025c0b15 100644 --- a/openrtb_ext/imp_telaria.go +++ b/openrtb_ext/imp_telaria.go @@ -1,6 +1,9 @@ package openrtb_ext +import "encoding/json" + type ExtImpTelaria struct { - AdCode string `json:"adCode,omitempty"` - SeatCode string `json:"seatCode"` + AdCode string `json:"adCode,omitempty"` + SeatCode string `json:"seatCode"` + Extra json.RawMessage `json:"extra,omitempty"` } From aaff1568c385f2e9b1c7bda32f4c037d81db5199 Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Wed, 24 Jun 2020 00:57:38 +0300 Subject: [PATCH 124/318] Adman adapter (#1356) Co-authored-by: Aiholkin --- adapters/adman/adman.go | 140 ++++++++++++++++++ adapters/adman/adman_test.go | 12 ++ .../admantest/exemplary/simple-banner.json | 134 +++++++++++++++++ .../admantest/exemplary/simple-video.json | 119 +++++++++++++++ .../exemplary/simple-web-banner.json | 133 +++++++++++++++++ adapters/adman/admantest/params/banner.json | 3 + .../adman/admantest/params/race/banner.json | 3 + .../adman/admantest/params/race/video.json | 3 + adapters/adman/admantest/params/video.json | 3 + .../admantest/supplemental/bad-imp-ext.json | 42 ++++++ .../admantest/supplemental/bad_response.json | 85 +++++++++++ .../admantest/supplemental/no-imp-ext-1.json | 39 +++++ .../admantest/supplemental/no-imp-ext-2.json | 39 +++++ .../admantest/supplemental/status-204.json | 79 ++++++++++ .../admantest/supplemental/status-404.json | 85 +++++++++++ adapters/adman/params_test.go | 46 ++++++ adapters/adman/usersync.go | 13 ++ adapters/adman/usersync_test.go | 35 +++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adman.go | 6 + static/bidder-info/adman.yaml | 11 ++ static/bidder-params/adman.json | 15 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 26 files changed, 1054 insertions(+) create mode 100644 adapters/adman/adman.go create mode 100644 adapters/adman/adman_test.go create mode 100644 adapters/adman/admantest/exemplary/simple-banner.json create mode 100644 adapters/adman/admantest/exemplary/simple-video.json create mode 100644 adapters/adman/admantest/exemplary/simple-web-banner.json create mode 100644 adapters/adman/admantest/params/banner.json create mode 100644 adapters/adman/admantest/params/race/banner.json create mode 100644 adapters/adman/admantest/params/race/video.json create mode 100644 adapters/adman/admantest/params/video.json create mode 100644 adapters/adman/admantest/supplemental/bad-imp-ext.json create mode 100644 adapters/adman/admantest/supplemental/bad_response.json create mode 100644 adapters/adman/admantest/supplemental/no-imp-ext-1.json create mode 100644 adapters/adman/admantest/supplemental/no-imp-ext-2.json create mode 100644 adapters/adman/admantest/supplemental/status-204.json create mode 100644 adapters/adman/admantest/supplemental/status-404.json create mode 100644 adapters/adman/params_test.go create mode 100644 adapters/adman/usersync.go create mode 100644 adapters/adman/usersync_test.go create mode 100644 openrtb_ext/imp_adman.go create mode 100644 static/bidder-info/adman.yaml create mode 100644 static/bidder-params/adman.json diff --git a/adapters/adman/adman.go b/adapters/adman/adman.go new file mode 100644 index 00000000000..aa8d0dc6e74 --- /dev/null +++ b/adapters/adman/adman.go @@ -0,0 +1,140 @@ +package adman + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// AdmanAdapter struct +type AdmanAdapter struct { + URI string +} + +// NewAdmanBidder Initializes the Bidder +func NewAdmanBidder(endpoint string) *AdmanAdapter { + return &AdmanAdapter{ + URI: endpoint, + } +} + +type admanParams struct { + TagID string `json:"TagID"` +} + +// MakeRequests create bid request for adman demand +func (a *AdmanAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var admanExt openrtb_ext.ExtImpAdman + var err error + + var adapterRequests []*adapters.RequestData + + reqCopy := *request + for _, imp := range request.Imp { + reqCopy.Imp = []openrtb.Imp{imp} + + var bidderExt adapters.ExtImpBidder + if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil { + errs = append(errs, err) + continue + } + + if err = json.Unmarshal(bidderExt.Bidder, &admanExt); err != nil { + errs = append(errs, err) + continue + } + + reqCopy.Imp[0].TagID = admanExt.TagID + + adapterReq, errors := a.makeRequest(&reqCopy) + if adapterReq != nil { + adapterRequests = append(adapterRequests, adapterReq) + } + errs = append(errs, errors...) + } + return adapterRequests, errs +} + +func (a *AdmanAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) { + + var errs []error + + reqJSON, err := json.Marshal(request) + + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + return &adapters.RequestData{ + Method: "POST", + Uri: a.URI, + Body: reqJSON, + Headers: headers, + }, errs +} + +// MakeBids makes the bids +func (a *AdmanAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusNotFound { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner == nil && imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } + return mediaType, nil + } + } + + // This shouldnt happen. Lets handle it just incase by returning an error. + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID), + } +} diff --git a/adapters/adman/adman_test.go b/adapters/adman/adman_test.go new file mode 100644 index 00000000000..da0f37e9a48 --- /dev/null +++ b/adapters/adman/adman_test.go @@ -0,0 +1,12 @@ +package adman + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + admanAdapter := NewAdmanBidder("http://eu-ams-1.admanmedia.com/?c=o&m=ortb") + adapterstest.RunJSONBidderTest(t, "admantest", admanAdapter) +} diff --git a/adapters/adman/admantest/exemplary/simple-banner.json b/adapters/adman/admantest/exemplary/simple-banner.json new file mode 100644 index 00000000000..41f76e00645 --- /dev/null +++ b/adapters/adman/admantest/exemplary/simple-banner.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "16", + "ext": { + "bidder": { + "TagID": "16" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "16", + "ext": { + "bidder": { + "TagID": "16" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "adman" + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adman/admantest/exemplary/simple-video.json b/adapters/adman/admantest/exemplary/simple-video.json new file mode 100644 index 00000000000..d7fa82d274d --- /dev/null +++ b/adapters/adman/admantest/exemplary/simple-video.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "TagID": "22" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "tagid": "22", + "ext": { + "bidder": { + "TagID": "22" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "adman" + } + ], + "cur": "USD" + } + } + } + ], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/adman/admantest/exemplary/simple-web-banner.json b/adapters/adman/admantest/exemplary/simple-web-banner.json new file mode 100644 index 00000000000..ce872bff52b --- /dev/null +++ b/adapters/adman/admantest/exemplary/simple-web-banner.json @@ -0,0 +1,133 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "adman" + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/adman/admantest/params/banner.json b/adapters/adman/admantest/params/banner.json new file mode 100644 index 00000000000..03fa8f3f2d8 --- /dev/null +++ b/adapters/adman/admantest/params/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "16" +} \ No newline at end of file diff --git a/adapters/adman/admantest/params/race/banner.json b/adapters/adman/admantest/params/race/banner.json new file mode 100644 index 00000000000..03fa8f3f2d8 --- /dev/null +++ b/adapters/adman/admantest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "16" +} \ No newline at end of file diff --git a/adapters/adman/admantest/params/race/video.json b/adapters/adman/admantest/params/race/video.json new file mode 100644 index 00000000000..e776c928a7e --- /dev/null +++ b/adapters/adman/admantest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "22" +} \ No newline at end of file diff --git a/adapters/adman/admantest/params/video.json b/adapters/adman/admantest/params/video.json new file mode 100644 index 00000000000..e776c928a7e --- /dev/null +++ b/adapters/adman/admantest/params/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "22" +} \ No newline at end of file diff --git a/adapters/adman/admantest/supplemental/bad-imp-ext.json b/adapters/adman/admantest/supplemental/bad-imp-ext.json new file mode 100644 index 00000000000..db3c8de5767 --- /dev/null +++ b/adapters/adman/admantest/supplemental/bad-imp-ext.json @@ -0,0 +1,42 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "16", + "ext": { + "adman": { + "TagID": "16" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, +"expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } +] +} diff --git a/adapters/adman/admantest/supplemental/bad_response.json b/adapters/adman/admantest/supplemental/bad_response.json new file mode 100644 index 00000000000..8c349297e73 --- /dev/null +++ b/adapters/adman/admantest/supplemental/bad_response.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 200, + "body": "" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/adman/admantest/supplemental/no-imp-ext-1.json b/adapters/adman/admantest/supplemental/no-imp-ext-1.json new file mode 100644 index 00000000000..8fad5ba5ef0 --- /dev/null +++ b/adapters/adman/admantest/supplemental/no-imp-ext-1.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "16", + "ext": "" + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/adman/admantest/supplemental/no-imp-ext-2.json b/adapters/adman/admantest/supplemental/no-imp-ext-2.json new file mode 100644 index 00000000000..337dfd044b3 --- /dev/null +++ b/adapters/adman/admantest/supplemental/no-imp-ext-2.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "16", + "ext": {} + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/adman/admantest/supplemental/status-204.json b/adapters/adman/admantest/supplemental/status-204.json new file mode 100644 index 00000000000..7f9a12dec29 --- /dev/null +++ b/adapters/adman/admantest/supplemental/status-204.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }] +} diff --git a/adapters/adman/admantest/supplemental/status-404.json b/adapters/adman/admantest/supplemental/status-404.json new file mode 100644 index 00000000000..560878342f0 --- /dev/null +++ b/adapters/adman/admantest/supplemental/status-404.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 404, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 404. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adman/params_test.go b/adapters/adman/params_test.go new file mode 100644 index 00000000000..a80c2a44b8b --- /dev/null +++ b/adapters/adman/params_test.go @@ -0,0 +1,46 @@ +package adman + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// TestValidParams makes sure that the adman schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdman, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adman params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the adman schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdman, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"TagID": "16"}`, +} + +var invalidParams = []string{ + `{"id": "123"}`, + `{"tagid": "123"}`, + `{"TagID": 16}`, +} diff --git a/adapters/adman/usersync.go b/adapters/adman/usersync.go new file mode 100644 index 00000000000..aae6afcdfcd --- /dev/null +++ b/adapters/adman/usersync.go @@ -0,0 +1,13 @@ +package adman + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +// NewAdmanSyncer returns adman syncer +func NewAdmanSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("adman", 149, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/adman/usersync_test.go b/adapters/adman/usersync_test.go new file mode 100644 index 00000000000..55a6e2cec97 --- /dev/null +++ b/adapters/adman/usersync_test.go @@ -0,0 +1,35 @@ +package adman + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestAdmanSyncer(t *testing.T) { + syncURL := "https://sync.admanmedia.com/pbs.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dadman%26uid%3D%5BUID%5D" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAdmanSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + Consent: "ANDFJDS", + }, + CCPA: ccpa.Policy{ + Value: "1-YY", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://sync.admanmedia.com/pbs.gif?gdpr=0&gdpr_consent=ANDFJDS&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dadman%26uid%3D%5BUID%5D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 149, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index c50118e2008..bb2c3191491 100755 --- a/config/config.go +++ b/config/config.go @@ -563,6 +563,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtarget, "https://sync.console.adtarget.com.tr/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtarget%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdtelligent, "https://sync.adtelligent.com/csync?t=p&ep=0&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Buid%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdmixer, "https://inv-nets.admixer.net/adxcm.aspx?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadmixer%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdman, "https://sync.admanmedia.com/pbs.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadman%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D") // openrtb_ext.BidderAdOcean doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAJA, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Daja%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25s") @@ -763,6 +764,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json") v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}") + v.SetDefault("adapters.adman.endpoint", "http://eu-ams-1.admanmedia.com/?c=o&m=ortb") v.SetDefault("adapters.admixer.endpoint", "http://inv-nets.admixer.net/pbs.aspx") v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 44054df06fd..c30bb0c622e 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -14,6 +14,7 @@ import ( "github.com/prebid/prebid-server/adapters/adhese" "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" + "github.com/prebid/prebid-server/adapters/adman" "github.com/prebid/prebid-server/adapters/admixer" "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adoppler" @@ -98,6 +99,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdhese: adhese.NewAdheseBidder(cfg.Adapters[string(openrtb_ext.BidderAdhese)].Endpoint), openrtb_ext.BidderAdkernel: adkernel.NewAdkernelAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernel))].Endpoint), openrtb_ext.BidderAdkernelAdn: adkernelAdn.NewAdkernelAdnAdapter(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdkernelAdn))].Endpoint), + openrtb_ext.BidderAdman: adman.NewAdmanBidder(cfg.Adapters[string(openrtb_ext.BidderAdman)].Endpoint), openrtb_ext.BidderAdmixer: admixer.NewAdmixerBidder(cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdmixer))].Endpoint), openrtb_ext.BidderAdOcean: adocean.NewAdOceanBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdOcean))].Endpoint), openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 1f9cffb9938..49d7b09d671 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -31,6 +31,7 @@ const ( BidderAdkernel BidderName = "adkernel" BidderAdkernelAdn BidderName = "adkernelAdn" BidderAdpone BidderName = "adpone" + BidderAdman BidderName = "adman" BidderAdmixer BidderName = "admixer" BidderAdOcean BidderName = "adocean" BidderAdtarget BidderName = "adtarget" @@ -110,6 +111,7 @@ var BidderMap = map[string]BidderName{ "adhese": BidderAdhese, "adkernel": BidderAdkernel, "adkernelAdn": BidderAdkernelAdn, + "adman": BidderAdman, "admixer": BidderAdmixer, "adocean": BidderAdOcean, "adpone": BidderAdpone, diff --git a/openrtb_ext/imp_adman.go b/openrtb_ext/imp_adman.go new file mode 100644 index 00000000000..bc79415452c --- /dev/null +++ b/openrtb_ext/imp_adman.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpAdman defines adman specifiec param +type ExtImpAdman struct { + TagID string `json:"TagID"` +} diff --git a/static/bidder-info/adman.yaml b/static/bidder-info/adman.yaml new file mode 100644 index 00000000000..932ef2e4242 --- /dev/null +++ b/static/bidder-info/adman.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "prebid@admanmedia.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-params/adman.json b/static/bidder-params/adman.json new file mode 100644 index 00000000000..90021e2cdfd --- /dev/null +++ b/static/bidder-params/adman.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adman Adapter Params", + "description": "A schema which validates params accepted by the Adman adapter", + + "type": "object", + "properties": { + "TagID": { + "type": "string", + "description": "An ID which identifies the adman ad tag" + } + }, + "required" : [ "TagID" ] + } + \ No newline at end of file diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 5657c8b7010..f1f643afb74 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -9,6 +9,7 @@ import ( "github.com/prebid/prebid-server/adapters/adform" "github.com/prebid/prebid-server/adapters/adkernel" "github.com/prebid/prebid-server/adapters/adkernelAdn" + "github.com/prebid/prebid-server/adapters/adman" "github.com/prebid/prebid-server/adapters/admixer" "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adpone" @@ -84,6 +85,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdform, adform.NewAdformSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernel, adkernel.NewAdkernelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdkernelAdn, adkernelAdn.NewAdkernelAdnSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAdman, adman.NewAdmanSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdmixer, admixer.NewAdmixerSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdOcean, adocean.NewAdOceanSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdpone, adpone.NewadponeSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 363cd491648..b23541eaf8a 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -18,6 +18,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdform): syncConfig, string(openrtb_ext.BidderAdkernel): syncConfig, string(openrtb_ext.BidderAdkernelAdn): syncConfig, + string(openrtb_ext.BidderAdman): syncConfig, string(openrtb_ext.BidderAdmixer): syncConfig, string(openrtb_ext.BidderAdOcean): syncConfig, string(openrtb_ext.BidderAdpone): syncConfig, From e376a8bbfcf513d65821f9c97547913d9a9c0d93 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Wed, 24 Jun 2020 14:08:14 -0400 Subject: [PATCH 125/318] =?UTF-8?q?PBS-632=20add=20max=20connections=20per?= =?UTF-8?q?=20host=20config=20setting=20to=20general=20http=20a=E2=80=A6?= =?UTF-8?q?=20(#1366)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 3 +++ config/config_test.go | 4 ++++ router/router.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/config/config.go b/config/config.go index bb2c3191491..7d34954583f 100755 --- a/config/config.go +++ b/config/config.go @@ -74,6 +74,7 @@ type Configuration struct { const MIN_COOKIE_SIZE_BYTES = 500 type HTTPClient struct { + MaxConnsPerHost int `mapstructure:"max_connections_per_host"` MaxIdleConns int `mapstructure:"max_idle_connections"` MaxIdleConnsPerHost int `mapstructure:"max_idle_connections_per_host"` IdleConnTimeout int `mapstructure:"idle_connection_timeout_seconds"` @@ -669,9 +670,11 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("host_cookie.value", "") v.SetDefault("host_cookie.ttl_days", 90) v.SetDefault("host_cookie.max_cookie_size_bytes", 0) + v.SetDefault("http_client.max_connections_per_host", 0) // unlimited v.SetDefault("http_client.max_idle_connections", 400) v.SetDefault("http_client.max_idle_connections_per_host", 10) v.SetDefault("http_client.idle_connection_timeout_seconds", 60) + v.SetDefault("http_client_cache.max_connections_per_host", 0) // unlimited v.SetDefault("http_client_cache.max_idle_connections", 10) v.SetDefault("http_client_cache.max_idle_connections_per_host", 2) v.SetDefault("http_client_cache.idle_connection_timeout_seconds", 60) diff --git a/config/config_test.go b/config/config_test.go index 2b291fe978d..c7d406cc8cc 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -67,10 +67,12 @@ external_cache: host: www.externalprebidcache.net path: endpoints/cache http_client: + max_connections_per_host: 10 max_idle_connections: 500 max_idle_connections_per_host: 20 idle_connection_timeout_seconds: 30 http_client_cache: + max_connections_per_host: 5 max_idle_connections: 1 max_idle_connections_per_host: 2 idle_connection_timeout_seconds: 3 @@ -217,9 +219,11 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "cache.query", cfg.CacheURL.Query, "uuid=%PBS_CACHE_UUID%") cmpStrings(t, "external_cache.host", cfg.ExtCacheURL.Host, "www.externalprebidcache.net") cmpStrings(t, "external_cache.path", cfg.ExtCacheURL.Path, "endpoints/cache") + cmpInts(t, "http_client.max_connections_per_host", cfg.Client.MaxConnsPerHost, 10) cmpInts(t, "http_client.max_idle_connections", cfg.Client.MaxIdleConns, 500) cmpInts(t, "http_client.max_idle_connections_per_host", cfg.Client.MaxIdleConnsPerHost, 20) cmpInts(t, "http_client.idle_connection_timeout_seconds", cfg.Client.IdleConnTimeout, 30) + cmpInts(t, "http_client_cache.max_connections_per_host", cfg.CacheClient.MaxConnsPerHost, 5) cmpInts(t, "http_client_cache.max_idle_connections", cfg.CacheClient.MaxIdleConns, 1) cmpInts(t, "http_client_cache.max_idle_connections_per_host", cfg.CacheClient.MaxIdleConnsPerHost, 2) cmpInts(t, "http_client_cache.idle_connection_timeout_seconds", cfg.CacheClient.IdleConnTimeout, 3) diff --git a/router/router.go b/router/router.go index 045c86ef25f..30936705a22 100644 --- a/router/router.go +++ b/router/router.go @@ -188,6 +188,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r generalHttpClient := &http.Client{ Transport: &http.Transport{ + MaxConnsPerHost: cfg.Client.MaxConnsPerHost, MaxIdleConns: cfg.Client.MaxIdleConns, MaxIdleConnsPerHost: cfg.Client.MaxIdleConnsPerHost, IdleConnTimeout: time.Duration(cfg.Client.IdleConnTimeout) * time.Second, @@ -197,6 +198,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r cacheHttpClient := &http.Client{ Transport: &http.Transport{ + MaxConnsPerHost: cfg.CacheClient.MaxConnsPerHost, MaxIdleConns: cfg.CacheClient.MaxIdleConns, MaxIdleConnsPerHost: cfg.CacheClient.MaxIdleConnsPerHost, IdleConnTimeout: time.Duration(cfg.CacheClient.IdleConnTimeout) * time.Second, From 16676360160b9a53286e8c8bcacf3454d923457d Mon Sep 17 00:00:00 2001 From: Marsel Date: Thu, 25 Jun 2020 17:29:25 +0300 Subject: [PATCH 126/318] Add ext.bidder.zoneid for Kubient adapater (#1367) * Add ext.bidder.zoneid for Kubient adapater * Check the number of Imps. zoneid is optional. --- adapters/kubient/kubient.go | 49 ++++++++++++++++--- .../kubient/kubienttest/exemplary/banner.json | 8 ++- .../kubient/kubienttest/exemplary/video.json | 8 ++- .../supplemental/bad_response.json | 8 ++- .../supplemental/missing-zoneid.json | 31 ++++++++++++ .../kubienttest/supplemental/no-imps.json | 12 +++++ .../kubienttest/supplemental/status_204.json | 2 + .../kubienttest/supplemental/status_400.json | 8 ++- openrtb_ext/imp_kubient.go | 6 +++ static/bidder-params/kubient.json | 8 ++- 10 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 adapters/kubient/kubienttest/supplemental/missing-zoneid.json create mode 100644 adapters/kubient/kubienttest/supplemental/no-imps.json create mode 100644 openrtb_ext/imp_kubient.go diff --git a/adapters/kubient/kubient.go b/adapters/kubient/kubient.go index cb1fe93ff82..acfaa44b6af 100644 --- a/adapters/kubient/kubient.go +++ b/adapters/kubient/kubient.go @@ -24,10 +24,24 @@ type KubientAdapter struct { func (adapter *KubientAdapter) MakeRequests( openRTBRequest *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo, -) ( - requestsToBidder []*adapters.RequestData, - errs []error, -) { +) ([]*adapters.RequestData, []error) { + if len(openRTBRequest.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impression in the bid request", + }} + } + errs := make([]error, 0, len(openRTBRequest.Imp)) + hasErrors := false + for _, impObj := range openRTBRequest.Imp { + err := checkImpExt(impObj) + if err != nil { + errs = append(errs, err) + hasErrors = true + } + } + if hasErrors { + return nil, errs + } openRTBRequestJSON, err := json.Marshal(openRTBRequest) if err != nil { errs = append(errs, err) @@ -36,17 +50,36 @@ func (adapter *KubientAdapter) MakeRequests( headers := http.Header{} headers.Add("Content-Type", "application/json;charset=utf-8") - requestToBidder := &adapters.RequestData{ + requestsToBidder := []*adapters.RequestData{{ Method: "POST", Uri: adapter.endpoint, Body: openRTBRequestJSON, Headers: headers, - } - requestsToBidder = append(requestsToBidder, requestToBidder) - + }} return requestsToBidder, errs } +func checkImpExt(impObj openrtb.Imp) error { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(impObj.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + var kubientExt openrtb_ext.ExtImpKubient + if err := json.Unmarshal(bidderExt.Bidder, &kubientExt); err != nil { + return &errortypes.BadInput{ + Message: "ext.bidder.zoneid is not provided", + } + } + if kubientExt.ZoneID == "" { + return &errortypes.BadInput{ + Message: "zoneid is empty", + } + } + return nil +} + // MakeBids makes the bids func (adapter *KubientAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { var errs []error diff --git a/adapters/kubient/kubienttest/exemplary/banner.json b/adapters/kubient/kubienttest/exemplary/banner.json index a32c761a7d0..9af4f9f8cfa 100644 --- a/adapters/kubient/kubienttest/exemplary/banner.json +++ b/adapters/kubient/kubienttest/exemplary/banner.json @@ -17,7 +17,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "9042" + } } } ] @@ -44,7 +46,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "9042" + } } } ] diff --git a/adapters/kubient/kubienttest/exemplary/video.json b/adapters/kubient/kubienttest/exemplary/video.json index 59d32874cec..d9346c3fa46 100644 --- a/adapters/kubient/kubienttest/exemplary/video.json +++ b/adapters/kubient/kubienttest/exemplary/video.json @@ -11,7 +11,9 @@ "h": 576 }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "9010" + } } } ] @@ -32,7 +34,9 @@ "h": 576 }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "9010" + } } } ] diff --git a/adapters/kubient/kubienttest/supplemental/bad_response.json b/adapters/kubient/kubienttest/supplemental/bad_response.json index 166743cf497..076acf29058 100644 --- a/adapters/kubient/kubienttest/supplemental/bad_response.json +++ b/adapters/kubient/kubienttest/supplemental/bad_response.json @@ -13,7 +13,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "23" + } } } ] @@ -36,7 +38,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "23" + } } } ] diff --git a/adapters/kubient/kubienttest/supplemental/missing-zoneid.json b/adapters/kubient/kubienttest/supplemental/missing-zoneid.json new file mode 100644 index 00000000000..cfd616621e2 --- /dev/null +++ b/adapters/kubient/kubienttest/supplemental/missing-zoneid.json @@ -0,0 +1,31 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-missing-req-param-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "zoneid is empty", + "comparison": "literal" + } + ] +} diff --git a/adapters/kubient/kubienttest/supplemental/no-imps.json b/adapters/kubient/kubienttest/supplemental/no-imps.json new file mode 100644 index 00000000000..189adf9a932 --- /dev/null +++ b/adapters/kubient/kubienttest/supplemental/no-imps.json @@ -0,0 +1,12 @@ +{ + "mockBidRequest": { + "id": "test-no-imp-request-id", + "imp": [] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the bid request", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kubient/kubienttest/supplemental/status_204.json b/adapters/kubient/kubienttest/supplemental/status_204.json index 58bb2629a5e..6794d58be6c 100644 --- a/adapters/kubient/kubienttest/supplemental/status_204.json +++ b/adapters/kubient/kubienttest/supplemental/status_204.json @@ -14,6 +14,7 @@ }, "ext": { "bidder": { + "zoneid": "203" } } } @@ -39,6 +40,7 @@ }, "ext": { "bidder": { + "zoneid": "203" } } } diff --git a/adapters/kubient/kubienttest/supplemental/status_400.json b/adapters/kubient/kubienttest/supplemental/status_400.json index e895f793dc1..29438cc3b8b 100644 --- a/adapters/kubient/kubienttest/supplemental/status_400.json +++ b/adapters/kubient/kubienttest/supplemental/status_400.json @@ -13,7 +13,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "102" + } } } ] @@ -37,7 +39,9 @@ ] }, "ext": { - "bidder": {} + "bidder": { + "zoneid": "102" + } } } ] diff --git a/openrtb_ext/imp_kubient.go b/openrtb_ext/imp_kubient.go new file mode 100644 index 00000000000..fafd2a0eb8f --- /dev/null +++ b/openrtb_ext/imp_kubient.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpKubient defines the contract for bidrequest.imp[i].ext.kubient +type ExtImpKubient struct { + ZoneID string `json:"zoneid"` +} diff --git a/static/bidder-params/kubient.json b/static/bidder-params/kubient.json index a75dd734ff2..9b975289a7b 100644 --- a/static/bidder-params/kubient.json +++ b/static/bidder-params/kubient.json @@ -3,5 +3,11 @@ "title": "Kubient Adapter Params", "description": "A schema which validates params accepted by the Kubient adapter", "type": "object", - "properties": { } + "properties": { + "zoneid": { + "type": "string", + "description": "Zone ID identifies Kubient placement ID.", + "minLength": 1 + } + } } From 8378a4529e66dc389167984607b8b4adf2a31dbf Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 29 Jun 2020 14:21:20 -0400 Subject: [PATCH 127/318] Improved IPv6 Support + Private Network Filtering (#1362) --- config/config.go | 41 ++- config/config_test.go | 74 +++- config/requestvalidation.go | 55 +++ config/requestvalidation_test.go | 145 ++++++++ endpoints/openrtb2/amp_auction.go | 9 +- endpoints/openrtb2/auction.go | 96 +++-- endpoints/openrtb2/auction_test.go | 196 ++++++++++- .../supplementary/site-has-ipv4.json | 38 ++ .../supplementary/site-has-ipv6.json | 38 ++ endpoints/openrtb2/video_auction.go | 24 +- endpoints/openrtb2/video_auction_test.go | 10 +- exchange/exchange_test.go | 15 +- main_test.go | 14 +- pbs/pbsrequest.go | 11 +- prebid/prebid.go | 82 ----- util/httputil/httputil.go | 99 ++++++ util/httputil/httputil_test.go | 327 ++++++++++++++++++ util/iputil/parse.go | 27 ++ util/iputil/parse_test.go | 30 ++ util/iputil/validator.go | 48 +++ util/iputil/validator_test.go | 222 ++++++++++++ 21 files changed, 1420 insertions(+), 181 deletions(-) create mode 100644 config/requestvalidation.go create mode 100644 config/requestvalidation_test.go create mode 100644 endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv4.json create mode 100644 endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv6.json delete mode 100644 prebid/prebid.go create mode 100644 util/httputil/httputil.go create mode 100644 util/httputil/httputil_test.go create mode 100644 util/iputil/parse.go create mode 100644 util/iputil/parse_test.go create mode 100644 util/iputil/validator.go create mode 100644 util/iputil/validator_test.go diff --git a/config/config.go b/config/config.go index 7d34954583f..50cfbb1c170 100755 --- a/config/config.go +++ b/config/config.go @@ -17,7 +17,7 @@ import ( validator "github.com/asaskevich/govalidator" ) -// Configuration +// Configuration specifies the static application config. type Configuration struct { ExternalURL string `mapstructure:"external_url"` Host string `mapstructure:"host"` @@ -69,6 +69,8 @@ type Configuration struct { RequestTimeoutHeaders RequestTimeoutHeaders `mapstructure:"request_timeout_headers"` // Debug/logging flags go here Debug Debug `mapstructure:"debug"` + // RequestValidation specifies the request validation options. + RequestValidation RequestValidation `mapstructure:"request_validation"` } const MIN_COOKIE_SIZE_BYTES = 500 @@ -239,15 +241,15 @@ type HostCookie struct { TTL int64 `mapstructure:"ttl_days"` } +func (cfg *HostCookie) TTLDuration() time.Duration { + return time.Duration(cfg.TTL) * time.Hour * 24 +} + type RequestTimeoutHeaders struct { RequestTimeInQueue string `mapstructure:"request_time_in_queue"` RequestTimeoutInQueue string `mapstructure:"request_timeout_in_queue"` } -func (cfg *HostCookie) TTLDuration() time.Duration { - return time.Duration(cfg.TTL) * time.Hour * 24 -} - const ( dummyHost string = "dummyhost.com" dummyPublisherID string = "12" @@ -498,6 +500,15 @@ func New(v *viper.Viper) (*Configuration, error) { } c.setDerivedDefaults() + if err := c.RequestValidation.Parse(); err != nil { + return nil, err + } + + if err := isValidCookieSize(c.HostCookie.MaxCookieSizeBytes); err != nil { + glog.Fatal(fmt.Printf("Max cookie size %d cannot be less than %d \n", c.HostCookie.MaxCookieSizeBytes, MIN_COOKIE_SIZE_BYTES)) + return nil, err + } + // To look for a request's publisher_id in the NonStandardPublishers list in // O(1) time, we fill this hash table located in the NonStandardPublisherMap field of GDPR c.GDPR.NonStandardPublisherMap = make(map[string]int) @@ -519,11 +530,6 @@ func New(v *viper.Viper) (*Configuration, error) { c.BlacklistedAcctMap[c.BlacklistedAccts[i]] = true } - if err := isValidCookieSize(c.HostCookie.MaxCookieSizeBytes); err != nil { - glog.Fatal(fmt.Printf("Max cookie size %d cannot be less than %d \n", c.HostCookie.MaxCookieSizeBytes, MIN_COOKIE_SIZE_BYTES)) - return nil, err - } - glog.Info("Logging the resolved configuration:") logGeneral(reflect.ValueOf(c), " \t") if errs := c.validate(); len(errs) > 0 { @@ -875,8 +881,23 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("debug.timeout_notification.sampling_rate", 0.0) v.SetDefault("debug.timeout_notification.fail_only", false) + /* IPv4 + /* Site Local: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 + /* Link Local: 169.254.0.0/16 + /* Loopback: 127.0.0.0/8 + /* + /* IPv6 + /* Loopback: ::1/128 + /* Unique Local: fc00::/7 + /* Link Local: fe80::/10 + /* Multicast: ff00::/8 + */ + v.SetDefault("request_validation.ipv4_private_networks", []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "127.0.0.0/8"}) + v.SetDefault("request_validation.ipv6_private_networks", []string{"::1/128", "fc00::/7", "fe80::/10", "ff00::/8"}) + // Set environment variable support: v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + v.SetTypeByDefaultValue(true) v.SetEnvPrefix("PBS") v.AutomaticEnv() v.ReadInConfig() diff --git a/config/config_test.go b/config/config_test.go index c7d406cc8cc..3456694db5c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2,7 +2,7 @@ package config import ( "bytes" - "fmt" + "net" "strings" "testing" "time" @@ -119,6 +119,9 @@ adapters: blacklisted_apps: ["spamAppID","sketchy-app-id"] account_required: true certificates_file: /etc/ssl/cert.pem +request_validation: + ipv4_private_networks: ["1.1.1.0/24"] + ipv6_private_networks: ["1111::/16", "2222::/16"] `) var adapterExtraInfoConfig = []byte(` @@ -292,6 +295,9 @@ func TestFullConfig(t *testing.T) { cmpBools(t, "account_required", cfg.AccountRequired, true) cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true) cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem") + cmpStrings(t, "request_validation.ipv4_private_networks", cfg.RequestValidation.IPv4PrivateNetworks[0], "1.1.1.0/24") + cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[0], "1111::/16") + cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[1], "2222::/16") } func TestUnmarshalAdapterExtraInfo(t *testing.T) { @@ -412,23 +418,63 @@ func TestLimitTimeout(t *testing.T) { } func TestCookieSizeError(t *testing.T) { - type aTest struct { - cookieHost *HostCookie + testCases := []struct { + description string + cookieSize int expectError bool + }{ + {"MIN_COOKIE_SIZE_BYTES + 1", MIN_COOKIE_SIZE_BYTES + 1, false}, + {"MIN_COOKIE_SIZE_BYTES", MIN_COOKIE_SIZE_BYTES, false}, + {"MIN_COOKIE_SIZE_BYTES - 1", MIN_COOKIE_SIZE_BYTES - 1, true}, + {"Zero", 0, false}, + {"Negative", -100, true}, } - testCases := []aTest{ - {cookieHost: &HostCookie{MaxCookieSizeBytes: 1 << 15}, expectError: false}, //32 KB, no error - {cookieHost: &HostCookie{MaxCookieSizeBytes: 800}, expectError: false}, - {cookieHost: &HostCookie{MaxCookieSizeBytes: 500}, expectError: false}, - {cookieHost: &HostCookie{MaxCookieSizeBytes: 0}, expectError: false}, - {cookieHost: &HostCookie{MaxCookieSizeBytes: 200}, expectError: true}, - {cookieHost: &HostCookie{MaxCookieSizeBytes: -100}, expectError: true}, + + for _, test := range testCases { + resultErr := isValidCookieSize(test.cookieSize) + + if test.expectError { + assert.Error(t, resultErr, test.description) + } else { + assert.NoError(t, resultErr, test.description) + } + } +} + +func TestNewCallsRequestValidation(t *testing.T) { + testCases := []struct { + description string + privateIPNetworks string + expectedError string + expectedIPs []net.IPNet + }{ + { + description: "Valid", + privateIPNetworks: `["1.1.1.0/24"]`, + expectedIPs: []net.IPNet{{IP: net.IP{1, 1, 1, 0}, Mask: net.CIDRMask(24, 32)}}, + }, + { + description: "Invalid", + privateIPNetworks: `["1"]`, + expectedError: "Invalid private IPv4 networks: '1'", + }, } - for i := range testCases { - if testCases[i].expectError { - assert.Error(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCookie.MaxCookieSizeBytes less than MIN_COOKIE_SIZE_BYTES = %d and not equal to zero should return an error", MIN_COOKIE_SIZE_BYTES)) + + for _, test := range testCases { + v := viper.New() + SetupViper(v, "") + v.SetConfigType("yaml") + v.ReadConfig(bytes.NewBuffer([]byte( + `request_validation: + ipv4_private_networks: ` + test.privateIPNetworks))) + + result, resultErr := New(v) + + if test.expectedError == "" { + assert.NoError(t, resultErr, test.description+":err") + assert.ElementsMatch(t, test.expectedIPs, result.RequestValidation.IPv4PrivateNetworksParsed, test.description+":parsed") } else { - assert.NoError(t, isValidCookieSize(testCases[i].cookieHost.MaxCookieSizeBytes), fmt.Sprintf("Configuration.HostCookie.MaxCookieSizeBytes greater than MIN_COOKIE_SIZE_BYTES = %d or equal to zero should not return an error", MIN_COOKIE_SIZE_BYTES)) + assert.Error(t, resultErr, test.description+":err") } } } diff --git a/config/requestvalidation.go b/config/requestvalidation.go new file mode 100644 index 00000000000..0824f4da880 --- /dev/null +++ b/config/requestvalidation.go @@ -0,0 +1,55 @@ +package config + +import ( + "errors" + "fmt" + "net" + "strings" +) + +// RequestValidation specifies the request validation options. +type RequestValidation struct { + IPv4PrivateNetworks []string `mapstructure:"ipv4_private_networks,flow"` + IPv4PrivateNetworksParsed []net.IPNet + + IPv6PrivateNetworks []string `mapstructure:"ipv6_private_networks,flow"` + IPv6PrivateNetworksParsed []net.IPNet +} + +// Parse converts the CIDR representation of the IPv4 and IPv6 private networks as net.IPNet structs, or returns an error if at least one is invalid. +func (r *RequestValidation) Parse() error { + ipv4Nets, err := parseNetworks(r.IPv4PrivateNetworks, net.IPv4len) + if err != nil { + return errors.New("Invalid private IPv4 network: " + err.Error()) + } + + ipv6Nets, err := parseNetworks(r.IPv6PrivateNetworks, net.IPv6len) + if err != nil { + return errors.New("Invalid private IPv6 network: " + err.Error()) + } + + r.IPv4PrivateNetworksParsed = ipv4Nets + r.IPv6PrivateNetworksParsed = ipv6Nets + return nil +} + +func parseNetworks(networks []string, networksLen int) ([]net.IPNet, error) { + ipNetworks := make([]net.IPNet, 0, len(networks)) + errMsg := strings.Builder{} + + for _, v := range networks { + v := strings.TrimSpace(v) + + if _, ipNet, err := net.ParseCIDR(v); err != nil || len(ipNet.IP) != networksLen { + fmt.Fprintf(&errMsg, "'%s',", v) + } else { + ipNetworks = append(ipNetworks, *ipNet) + } + } + + if errMsg.Len() > 0 { + return nil, errors.New(errMsg.String()[:errMsg.Len()-1]) + } + + return ipNetworks, nil +} diff --git a/config/requestvalidation_test.go b/config/requestvalidation_test.go new file mode 100644 index 00000000000..cacb4f2d140 --- /dev/null +++ b/config/requestvalidation_test.go @@ -0,0 +1,145 @@ +package config + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParse(t *testing.T) { + ipv4Mask16 := net.CIDRMask(16, 32) + ipv4Mask24 := net.CIDRMask(24, 32) + + ipv6Mask16 := net.CIDRMask(16, 128) + ipv6Mask32 := net.CIDRMask(32, 128) + + testCases := []struct { + description string + ipv4 []string + ipv4Expected []net.IPNet + ipv6 []string + ipv6Expected []net.IPNet + expectedErr string + }{ + { + description: "Empty", + ipv4: []string{}, + ipv4Expected: []net.IPNet{}, + ipv6: []string{}, + ipv6Expected: []net.IPNet{}, + }, + { + description: "One", + ipv4: []string{"1.1.1.1/24"}, + ipv4Expected: []net.IPNet{{IP: net.IP{1, 1, 1, 0}, Mask: ipv4Mask24}}, + ipv6: []string{"1111:2222::/16"}, + ipv6Expected: []net.IPNet{{IP: net.ParseIP("1111::"), Mask: ipv6Mask16}}, + }, + { + description: "One - Ignore Whitespace", + ipv4: []string{" 1.1.1.1/24 "}, + ipv4Expected: []net.IPNet{{IP: net.IP{1, 1, 1, 0}, Mask: ipv4Mask24}}, + ipv6: []string{" 1111:2222::/16 "}, + ipv6Expected: []net.IPNet{{IP: net.ParseIP("1111::"), Mask: ipv6Mask16}}, + }, + { + description: "Many", + ipv4: []string{"1.1.1.1/24", "2.2.2.2/16"}, + ipv4Expected: []net.IPNet{{IP: net.IP{1, 1, 1, 0}, Mask: ipv4Mask24}, {IP: net.IP{2, 2, 0, 0}, Mask: ipv4Mask16}}, + ipv6: []string{"1111:2222::/16", "1111:2222:3333::/32"}, + ipv6Expected: []net.IPNet{{IP: net.ParseIP("1111::"), Mask: ipv6Mask16}, {IP: net.ParseIP("1111:2222::"), Mask: ipv6Mask32}}, + }, + { + description: "Malformed - IPv4 - One", + ipv4: []string{"malformed1"}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: 'malformed1'", + }, + { + description: "Malformed - IPv4 - Many", + ipv4: []string{"malformed1", "malformed2"}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: 'malformed1','malformed2'", + }, + { + description: "Malformed - IPv6 - One", + ipv4: []string{}, + ipv6: []string{"malformed2"}, + expectedErr: "Invalid private IPv6 network: 'malformed2'", + }, + { + description: "Malformed - IPv6 - Many", + ipv4: []string{}, + ipv6: []string{"malformed1", "malformed2"}, + expectedErr: "Invalid private IPv6 network: 'malformed1','malformed2'", + }, + { + description: "Malformed - Mixed", + ipv4: []string{"malformed1"}, + ipv6: []string{"malformed2"}, + expectedErr: "Invalid private IPv4 network: 'malformed1'", + }, + { + description: "Malformed - IPv4 - Ignore Whitespace", + ipv4: []string{" malformed1 "}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: 'malformed1'", + }, + { + description: "Malformed - IPv6 - Ignore Whitespace", + ipv4: []string{}, + ipv6: []string{" malformed2 "}, + expectedErr: "Invalid private IPv6 network: 'malformed2'", + }, + { + description: "Malformed - IPv4 - Missing Network Mask", + ipv4: []string{"1.1.1.1"}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: '1.1.1.1'", + }, + { + description: "Malformed - IPv6 - Missing Network Mask", + ipv4: []string{}, + ipv6: []string{"1111::"}, + expectedErr: "Invalid private IPv6 network: '1111::'", + }, + { + description: "Malformed - IPv4 - Wrong IP Version", + ipv4: []string{"1111::/16"}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: '1111::/16'", + }, + { + description: "Malformed - IPv6 - Wrong IP Version", + ipv4: []string{}, + ipv6: []string{"1.1.1.1/16"}, + expectedErr: "Invalid private IPv6 network: '1.1.1.1/16'", + }, + { + description: "Malformed - IPv6 Mapped IPv4", + ipv4: []string{"::FFFF:1.1.1.1"}, + ipv6: []string{}, + expectedErr: "Invalid private IPv4 network: '::FFFF:1.1.1.1'", + }, + } + + for _, test := range testCases { + requestValidation := &RequestValidation{ + IPv4PrivateNetworks: test.ipv4, + IPv6PrivateNetworks: test.ipv6, + } + + err := requestValidation.Parse() + + if test.expectedErr == "" { + assert.NoError(t, err, test.description+":err") + } else { + assert.Error(t, err, test.description+":err") + assert.Equal(t, test.expectedErr, err.Error(), test.description+":err_msg") + } + + assert.ElementsMatch(t, requestValidation.IPv4PrivateNetworksParsed, test.ipv4Expected, test.description+":ipv4") + assert.ElementsMatch(t, requestValidation.IPv6PrivateNetworksParsed, test.ipv6Expected, test.description+":ipv6") + } +} diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 2dcd572c63c..e8b5d3ecc76 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -26,6 +26,7 @@ import ( "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/util/iputil" ) const defaultAmpRequestTimeoutMillis = 900 @@ -58,6 +59,11 @@ func NewAmpEndpoint( defRequest := defReqJSON != nil && len(defReqJSON) > 0 + ipValidator := iputil.PublicNetworkIPValidator{ + IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed, + IPv6PrivateNetworks: cfg.RequestValidation.IPv6PrivateNetworksParsed, + } + return httprouter.Handle((&endpointDeps{ ex, validator, @@ -72,7 +78,8 @@ func NewAmpEndpoint( defReqJSON, bidderMap, nil, - nil}).AmpAuction), nil + nil, + ipValidator}).AmpAuction), nil } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index bd50fca9149..20acc2aedd3 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -27,12 +27,13 @@ import ( "github.com/prebid/prebid-server/exchange" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" - "github.com/prebid/prebid-server/prebid" "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/privacy/ccpa" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/util/httputil" + "github.com/prebid/prebid-server/util/iputil" "golang.org/x/net/publicsuffix" ) @@ -43,8 +44,14 @@ func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidato if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { return nil, errors.New("NewEndpoint requires non-nil arguments.") } + defRequest := defReqJSON != nil && len(defReqJSON) > 0 + ipValidator := iputil.PublicNetworkIPValidator{ + IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed, + IPv6PrivateNetworks: cfg.RequestValidation.IPv6PrivateNetworksParsed, + } + return httprouter.Handle((&endpointDeps{ ex, validator, @@ -59,24 +66,26 @@ func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidato defReqJSON, bidderMap, nil, - nil}).Auction), nil + nil, + ipValidator}).Auction), nil } type endpointDeps struct { - ex exchange.Exchange - paramsValidator openrtb_ext.BidderParamValidator - storedReqFetcher stored_requests.Fetcher - videoFetcher stored_requests.Fetcher - categories stored_requests.CategoryFetcher - cfg *config.Configuration - metricsEngine pbsmetrics.MetricsEngine - analytics analytics.PBSAnalyticsModule - disabledBidders map[string]string - defaultRequest bool - defReqJSON []byte - bidderMap map[string]openrtb_ext.BidderName - cache prebid_cache_client.Client - debugLogRegexp *regexp.Regexp + ex exchange.Exchange + paramsValidator openrtb_ext.BidderParamValidator + storedReqFetcher stored_requests.Fetcher + videoFetcher stored_requests.Fetcher + categories stored_requests.CategoryFetcher + cfg *config.Configuration + metricsEngine pbsmetrics.MetricsEngine + analytics analytics.PBSAnalyticsModule + disabledBidders map[string]string + defaultRequest bool + defReqJSON []byte + bidderMap map[string]openrtb_ext.BidderName + cache prebid_cache_client.Client + debugLogRegexp *regexp.Regexp + privateNetworkIPValidator iputil.IPValidator } func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { @@ -308,17 +317,14 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { return errL } - ccpaPolicy, ccpaPolicyErr := ccpa.ReadPolicy(req) - if ccpaPolicyErr != nil { - errL = append(errL, ccpaPolicyErr) + if policy, err := ccpa.ReadPolicy(req); err != nil { + errL = append(errL, errL...) return errL - } - - if err := ccpaPolicy.Validate(); err != nil { + } else if err := policy.Validate(); err != nil { errL = append(errL, &errortypes.InvalidPrivacyConsent{Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err)}) - ccpaPolicy.Value = "" - if err := ccpaPolicy.Write(req); err != nil { + policy.Value = "" + if err := policy.Write(req); err != nil { errL = append(errL, fmt.Errorf("Unable to remove invalid CCPA consent from the request. (%v)", err)) } } @@ -914,13 +920,27 @@ func validateRegs(regs *openrtb.Regs) error { return nil } +func sanitizeRequest(r *openrtb.BidRequest, ipValidator iputil.IPValidator) { + if r.Device != nil { + if ip, ver := iputil.ParseIP(r.Device.IP); ip == nil || ver != iputil.IPv4 || !ipValidator.IsValid(ip, ver) { + r.Device.IP = "" + } + + if ip, ver := iputil.ParseIP(r.Device.IPv6); ip == nil || ver != iputil.IPv6 || !ipValidator.IsValid(ip, ver) { + r.Device.IPv6 = "" + } + } +} + // setFieldsImplicitly uses _implicit_ information from the httpReq to set values on bidReq. // This function does not consume the request body, which was set explicitly, but infers certain // OpenRTB properties from the headers and other implicit info. // // This function _should not_ override any fields which were defined explicitly by the caller in the request. func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { - setDeviceImplicitly(httpReq, bidReq) + sanitizeRequest(bidReq, deps.privateNetworkIPValidator) + + setDeviceImplicitly(httpReq, bidReq, deps.privateNetworkIPValidator) // Per the OpenRTB spec: A bid request must not contain both a Site and an App object. if bidReq.App == nil { @@ -932,8 +952,8 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, bidReq *ope } // setDeviceImplicitly uses implicit info from httpReq to populate bidReq.Device -func setDeviceImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { - setIPImplicitly(httpReq, bidReq) // Fixes #230 +func setDeviceImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest, ipValidtor iputil.IPValidator) { + setIPImplicitly(httpReq, bidReq, ipValidtor) setUAImplicitly(httpReq, bidReq) } @@ -975,7 +995,7 @@ func setSiteImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { func setImpsImplicitly(httpReq *http.Request, imps []openrtb.Imp) { secure := int8(1) for i := 0; i < len(imps); i++ { - if imps[i].Secure == nil && prebid.IsSecure(httpReq) { + if imps[i].Secure == nil && httputil.IsSecure(httpReq) { imps[i].Secure = &secure } } @@ -1132,13 +1152,21 @@ func getStoredRequestId(data []byte) (string, bool, error) { } // setIPImplicitly sets the IP address on bidReq, if it's not explicitly defined and we can figure it out. -func setIPImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { - if bidReq.Device == nil || bidReq.Device.IP == "" { - if ip := prebid.GetIP(httpReq); ip != "" { - if bidReq.Device == nil { - bidReq.Device = &openrtb.Device{} +func setIPImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest, ipValidator iputil.IPValidator) { + if bidReq.Device == nil || (bidReq.Device.IP == "" && bidReq.Device.IPv6 == "") { + if ip, ver := httputil.FindIP(httpReq, ipValidator); ip != nil { + switch ver { + case iputil.IPv4: + if bidReq.Device == nil { + bidReq.Device = &openrtb.Device{} + } + bidReq.Device.IP = ip.String() + case iputil.IPv6: + if bidReq.Device == nil { + bidReq.Device = &openrtb.Device{} + } + bidReq.Device.IPv6 = ip.String() } - bidReq.Device.IP = ip } } } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index c3b9267bf8b..97f0038a392 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httptest" "os" @@ -29,6 +30,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/util/iputil" "github.com/stretchr/testify/assert" ) @@ -526,26 +528,79 @@ func TestAuctionTypeDefault(t *testing.T) { } } -// TestImplicitIPs prevents #230 -func TestImplicitIPs(t *testing.T) { - ex := &nobidExchange{} - // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. - // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. - theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - endpoint, _ := NewEndpoint(ex, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) +func TestImplicitIPsEndToEnd(t *testing.T) { + testCases := []struct { + description string + reqJSONFile string + xForwardedForHeader string + privateNetworksIPv4 []net.IPNet + privateNetworksIPv6 []net.IPNet + expectedDeviceIPv4 string + expectedDeviceIPv6 string + }{ + { + description: "IPv4", + reqJSONFile: "site.json", + xForwardedForHeader: "1.1.1.1", + expectedDeviceIPv4: "1.1.1.1", + }, + { + description: "IPv6", + reqJSONFile: "site.json", + xForwardedForHeader: "1111::", + expectedDeviceIPv6: "1111::", + }, + { + description: "IPv4 - Defined In Request", + reqJSONFile: "site-has-ipv4.json", + xForwardedForHeader: "1.1.1.1", + expectedDeviceIPv4: "8.8.8.8", // Hardcoded value in test file. + }, + { + description: "IPv6 - Defined In Request", + reqJSONFile: "site-has-ipv6.json", + xForwardedForHeader: "1111::", + expectedDeviceIPv6: "8888::", // Hardcoded value in test file. + }, + { + description: "IPv4 - Defined In Request - Private Network", + reqJSONFile: "site-has-ipv4.json", + xForwardedForHeader: "1.1.1.1", + privateNetworksIPv4: []net.IPNet{{IP: net.IP{8, 8, 8, 0}, Mask: net.CIDRMask(24, 32)}}, // Hardcoded value in test file. + expectedDeviceIPv4: "1.1.1.1", + }, + { + description: "IPv6 - Defined In Request - Private Network", + reqJSONFile: "site-has-ipv6.json", + xForwardedForHeader: "1111::", + privateNetworksIPv6: []net.IPNet{{IP: net.ParseIP("8800::"), Mask: net.CIDRMask(8, 128)}}, // Hardcoded value in test file. + expectedDeviceIPv6: "1111::", + }, + } - httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json"))) - httpReq.Header.Set("X-Forwarded-For", "123.456.78.90") - recorder := httptest.NewRecorder() + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + for _, test := range testCases { + exchange := &nobidExchange{} + cfg := &config.Configuration{ + MaxRequestSize: maxSize, + RequestValidation: config.RequestValidation{ + IPv4PrivateNetworksParsed: test.privateNetworksIPv4, + IPv6PrivateNetworksParsed: test.privateNetworksIPv6, + }, + } + endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, cfg, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) - endpoint(recorder, httpReq, nil) + httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile))) + httpReq.Header.Set("X-Forwarded-For", test.xForwardedForHeader) - if ex.gotRequest == nil { - t.Fatalf("The request never made it into the Exchange.") - } + endpoint(httptest.NewRecorder(), httpReq, nil) - if ex.gotRequest.Device.IP != "123.456.78.90" { - t.Errorf("Bad device IP. Expected 123.456.78.90, got %s", ex.gotRequest.Device.IP) + result := exchange.gotRequest + if !assert.NotEmpty(t, result, test.description+"Request received by the exchange.") { + t.FailNow() + } + assert.Equal(t, test.expectedDeviceIPv4, result.Device.IP, test.description+":ipv4") + assert.Equal(t, test.expectedDeviceIPv6, result.Device.IPv6, test.description+":ipv6") } } @@ -602,10 +657,26 @@ func TestStoredRequests(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - edep := &endpointDeps{&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, false, []byte{}, openrtb_ext.BidderMap, nil, nil} + deps := &endpointDeps{ + &nobidExchange{}, + newParamsValidator(t), + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + theMetrics, + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BidderMap, + nil, + nil, + hardcodedResponseIPValidator{response: true}, + } for i, requestData := range testStoredRequests { - newRequest, errList := edep.processStoredRequests(context.Background(), json.RawMessage(requestData)) + newRequest, errList := deps.processStoredRequests(context.Background(), json.RawMessage(requestData)) if len(errList) != 0 { for _, err := range errList { if err != nil { @@ -640,6 +711,7 @@ func TestOversizedRequest(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -674,6 +746,7 @@ func TestRequestSizeEdgeCase(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -813,6 +886,7 @@ func TestDisabledBidder(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -848,6 +922,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } errs := deps.validateImpExt(imp, nil, 0) assert.JSONEq(t, `{"appnexus":{"placement_id":555}}`, string(imp.Ext)) @@ -888,6 +963,7 @@ func TestCurrencyTrunc(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } ui := uint64(1) @@ -931,6 +1007,7 @@ func TestCCPAInvalid(t *testing.T) { openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } ui := uint64(1) @@ -962,6 +1039,81 @@ func TestCCPAInvalid(t *testing.T) { assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request") } +func TestSanitizeRequest(t *testing.T) { + testCases := []struct { + description string + req *openrtb.BidRequest + ipValidator iputil.IPValidator + expectedIPv4 string + expectedIPv6 string + }{ + { + description: "Empty", + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "", + IPv6: "", + }, + }, + expectedIPv4: "", + expectedIPv6: "", + }, + { + description: "Valid", + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + IPv6: "1111::", + }, + }, + ipValidator: hardcodedResponseIPValidator{response: true}, + expectedIPv4: "1.1.1.1", + expectedIPv6: "1111::", + }, + { + description: "Invalid", + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1.1.1.1", + IPv6: "1111::", + }, + }, + ipValidator: hardcodedResponseIPValidator{response: false}, + expectedIPv4: "", + expectedIPv6: "", + }, + { + description: "Invalid - Wrong IP Types", + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "1111::", + IPv6: "1.1.1.1", + }, + }, + ipValidator: hardcodedResponseIPValidator{response: true}, + expectedIPv4: "", + expectedIPv6: "", + }, + { + description: "Malformed", + req: &openrtb.BidRequest{ + Device: &openrtb.Device{ + IP: "malformed", + IPv6: "malformed", + }, + }, + expectedIPv4: "", + expectedIPv6: "", + }, + } + + for _, test := range testCases { + sanitizeRequest(test.req, test.ipValidator) + assert.Equal(t, test.expectedIPv4, test.req.Device.IP, test.description+":ipv4") + assert.Equal(t, test.expectedIPv6, test.req.Device.IPv6, test.description+":ipv6") + } +} + // nobidExchange is a well-behaved exchange which always bids "no bid". type nobidExchange struct { gotRequest *openrtb.BidRequest @@ -1385,3 +1537,11 @@ func newBidderInfo(cfg config.Adapter) adapters.BidderInfo { Status: status, } } + +type hardcodedResponseIPValidator struct { + response bool +} + +func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool { + return v.response +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv4.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv4.json new file mode 100644 index 00000000000..feade898833 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv4.json @@ -0,0 +1,38 @@ +{ + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 600 + }] + }, + "pmp": { + "deals": [{ + "id": "some-deal-id" + }] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + }], + "device": { + "ip": "8.8.8.8" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": "low" + }, + "cache": { + "bids": {} + } + } + } +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv6.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv6.json new file mode 100644 index 00000000000..42d8d37b1cb --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-ipv6.json @@ -0,0 +1,38 @@ +{ + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 600 + }] + }, + "pmp": { + "deals": [{ + "id": "some-deal-id" + }] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + }], + "device": { + "ipv6": "8888::" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": "low" + }, + "cache": { + "bids": {} + } + } + } + } \ No newline at end of file diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 64c99fa5a3e..18678be541c 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -18,6 +18,7 @@ import ( jsonpatch "github.com/evanphx/json-patch" "github.com/gofrs/uuid" "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/util/iputil" "github.com/golang/glog" "github.com/julienschmidt/httprouter" @@ -39,11 +40,32 @@ func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamVal if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { return nil, errors.New("NewVideoEndpoint requires non-nil arguments.") } + defRequest := defReqJSON != nil && len(defReqJSON) > 0 + ipValidator := iputil.PublicNetworkIPValidator{ + IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed, + IPv6PrivateNetworks: cfg.RequestValidation.IPv6PrivateNetworksParsed, + } + videoEndpointRegexp := regexp.MustCompile(`[<>]`) - return httprouter.Handle((&endpointDeps{ex, validator, requestsById, videoFetcher, categories, cfg, met, pbsAnalytics, disabledBidders, defRequest, defReqJSON, bidderMap, cache, videoEndpointRegexp}).VideoAuctionEndpoint), nil + return httprouter.Handle((&endpointDeps{ + ex, + validator, + requestsById, + videoFetcher, + categories, + cfg, + met, + pbsAnalytics, + disabledBidders, + defRequest, + defReqJSON, + bidderMap, + cache, + videoEndpointRegexp, + ipValidator}).VideoAuctionEndpoint), nil } /* diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 631cb277f7f..f29ac3bfed9 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -1110,7 +1110,7 @@ func TestCCPA(t *testing.T) { func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} - edep := &endpointDeps{ + deps := &endpointDeps{ ex, newParamsValidator(t), &mockVideoStoredReqFetcher{}, @@ -1125,9 +1125,10 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *p openrtb_ext.BidderMap, nil, nil, + hardcodedResponseIPValidator{response: true}, } - return edep, theMetrics, mockModule + return deps, theMetrics, mockModule } type mockAnalyticsModule struct { @@ -1151,7 +1152,7 @@ func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { return } func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - edep := &endpointDeps{ + deps := &endpointDeps{ ex, newParamsValidator(t), &mockVideoStoredReqFetcher{}, @@ -1166,9 +1167,10 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { openrtb_ext.BidderMap, ex.cache, regexp.MustCompile(`[<>]`), + hardcodedResponseIPValidator{response: true}, } - return edep + return deps } type mockCacheClient struct { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 93cb60fb5af..161b24fd1c1 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -16,19 +16,18 @@ import ( "time" "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currencies" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" - - "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/gdpr" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" metricsConf "github.com/prebid/prebid-server/pbsmetrics/config" pbc "github.com/prebid/prebid-server/prebid_cache_client" + "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" + + "github.com/buger/jsonparser" + "github.com/mxmCherry/openrtb" "github.com/rcrowley/go-metrics" "github.com/stretchr/testify/assert" "github.com/yudai/gojsondiff" @@ -1837,7 +1836,7 @@ func (c *wellBehavedCache) GetExtCacheData() (string, string) { return "www.pbcserver.com", "/pbcache/endpoint" } -func (c *wellBehavedCache) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) { +func (c *wellBehavedCache) PutJson(ctx context.Context, values []pbc.Cacheable) ([]string, []error) { ids := make([]string, len(values)) for i := 0; i < len(values); i++ { ids[i] = strconv.Itoa(i) diff --git a/main_test.go b/main_test.go index d7dc9dd24a0..70eea2825f0 100644 --- a/main_test.go +++ b/main_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/prebid/prebid-server/config" + "github.com/stretchr/testify/assert" "github.com/spf13/viper" ) @@ -56,10 +57,11 @@ func TestViperEnv(t *testing.T) { ttl := forceEnv(t, "PBS_HOST_COOKIE_TTL_DAYS", "60") defer ttl() - // Basic config set - compareStrings(t, "Viper error: port expected to be %s, found %s", "7777", v.Get("port").(string)) - // Nested config set - compareStrings(t, "Viper error: adapters.pubmatic.endpoint expected to be %s, found %s", "not_an_endpoint", v.Get("adapters.pubmatic.endpoint").(string)) - // Config set with underscores - compareStrings(t, "Viper error: host_cookie.ttl_days expected to be %s, found %s", "60", v.Get("host_cookie.ttl_days").(string)) + ipv4Networks := forceEnv(t, "PBS_REQUEST_VALIDATION_IPV4_PRIVATE_NETWORKS", "1.1.1.1/24 2.2.2.2/24") + defer ipv4Networks() + + assert.Equal(t, 7777, v.Get("port"), "Basic Config") + assert.Equal(t, "not_an_endpoint", v.Get("adapters.pubmatic.endpoint"), "Nested Config") + assert.Equal(t, 60, v.Get("host_cookie.ttl_days"), "Config With Underscores") + assert.ElementsMatch(t, []string{"1.1.1.1/24", "2.2.2.2/24"}, v.Get("request_validation.ipv4_private_networks"), "Arrays") } diff --git a/pbs/pbsrequest.go b/pbs/pbsrequest.go index 9e79b62b38b..30f8bd25c0d 100644 --- a/pbs/pbsrequest.go +++ b/pbs/pbsrequest.go @@ -12,9 +12,10 @@ import ( "github.com/prebid/prebid-server/cache" "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/prebid" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/util/httputil" + "github.com/prebid/prebid-server/util/iputil" "github.com/blang/semver" "github.com/buger/jsonparser" @@ -216,6 +217,8 @@ func ParseMediaTypes(types []string) []MediaType { return mtypes } +var ipv4Validator iputil.IPValidator = iputil.VersionIPValidator{iputil.IPv4} + func ParsePBSRequest(r *http.Request, cfg *config.AuctionTimeouts, cache cache.Cache, hostCookieConfig *config.HostCookie) (*PBSRequest, error) { defer r.Body.Close() @@ -235,7 +238,9 @@ func ParsePBSRequest(r *http.Request, cfg *config.AuctionTimeouts, cache cache.C if pbsReq.Device == nil { pbsReq.Device = &openrtb.Device{} } - pbsReq.Device.IP = prebid.GetIP(r) + if ip, _ := httputil.FindIP(r, ipv4Validator); ip != nil { + pbsReq.Device.IP = ip.String() + } if pbsReq.SDK == nil { pbsReq.SDK = &SDK{} @@ -291,7 +296,7 @@ func ParsePBSRequest(r *http.Request, cfg *config.AuctionTimeouts, cache cache.C pbsReq.IsDebug = true } - if prebid.IsSecure(r) { + if httputil.IsSecure(r) { pbsReq.Secure = 1 } diff --git a/prebid/prebid.go b/prebid/prebid.go deleted file mode 100644 index 68c4a48c2c8..00000000000 --- a/prebid/prebid.go +++ /dev/null @@ -1,82 +0,0 @@ -package prebid - -import ( - "net" - "net/http" - "strings" -) - -var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") -var xRealIP = http.CanonicalHeaderKey("X-Real-IP") -var xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") - -// IsSecure attempts to detect whether the request is https -func IsSecure(r *http.Request) bool { - // lowercase for case-insensitive match for X-Forwarded-Proto header - if strings.ToLower(r.Header.Get(xForwardedProto)) == "https" { - return true - } - // ensure that URL.Scheme is lowercase (it should be "https") - if strings.ToLower(r.URL.Scheme) == "https" { - return true - } - // use strings.HasPrefix because a valid example is "HTTP/1.0" - if strings.HasPrefix(r.Proto, "HTTPS") { - return true - } - // check if TLS is not-nil as a final fallback - if r.TLS != nil { - return true - } - return false -} - -// GetIP will attempt to get the IP Address by first checking headers -// and then falling back on the RemoteAddr -func GetIP(r *http.Request) string { - // first check headers - if ip := GetForwardedIP(r); ip != "" { - return ip - } - // next try to parse the RemoteAddr. - // if err is not nil then weird hosts might appear as the ip: https://github.com/golang/go/issues/14827 - if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - return ip - } - return "" -} - -// GetForwardedIP will return back X-Forwarded-For or X-Real-IP (if set) -func GetForwardedIP(r *http.Request) string { - // first attempt to parse X-Forwarded-For - if ip := getForwardedFor(r); ip != "" { - return ip - } - // if we don't have X-Forwarded-For then try X-Real-IP - if ip := getRealIP(r); ip != "" { - return ip - } - return "" -} - -// getForwardedFor will attempt to parse the X-Forwarded-For header -func getForwardedFor(r *http.Request) string { - if xff := r.Header.Get(xForwardedFor); xff != "" { - // X-Forwarded-For: client1, proxy1, proxy2 - i := strings.Index(xff, ", ") - if i == -1 { - i = len(xff) - } - return xff[:i] - } - return "" -} - -// getRealIP will attempt to parse the X-Real-IP header -// Header.Get is case-insensitive -func getRealIP(r *http.Request) string { - if xrip := r.Header.Get(xRealIP); xrip != "" { - return xrip - } - return "" -} diff --git a/util/httputil/httputil.go b/util/httputil/httputil.go new file mode 100644 index 00000000000..461512771b3 --- /dev/null +++ b/util/httputil/httputil.go @@ -0,0 +1,99 @@ +package httputil + +import ( + "net" + "net/http" + "strings" + + "github.com/prebid/prebid-server/util/iputil" +) + +var ( + trueClientIP = http.CanonicalHeaderKey("True-Client-IP") + xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto") + xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") + xRealIP = http.CanonicalHeaderKey("X-Real-IP") +) + +const ( + https = "https" +) + +// IsSecure determines if a http request uses https. +func IsSecure(r *http.Request) bool { + if strings.EqualFold(r.Header.Get(xForwardedProto), https) { + return true + } + + if strings.EqualFold(r.URL.Scheme, https) { + return true + } + + if r.TLS != nil { + return true + } + + return false +} + +// FindIP returns the first ip address found in the http request matching the predicate v. +func FindIP(r *http.Request, v iputil.IPValidator) (net.IP, iputil.IPVersion) { + if ip, ver := findTrueClientIP(r, v); ip != nil { + return ip, ver + } + + if ip, ver := findForwardedFor(r, v); ip != nil { + return ip, ver + } + + if ip, ver := findRealIP(r, v); ip != nil { + return ip, ver + } + + if ip, ver := findRemoteAddr(r, v); ip != nil { + return ip, ver + } + + return nil, iputil.IPvUnknown +} + +func findTrueClientIP(r *http.Request, v iputil.IPValidator) (net.IP, iputil.IPVersion) { + if value := r.Header.Get(trueClientIP); value != "" { + value = strings.TrimSpace(value) + if ip, ver := iputil.ParseIP(value); ip != nil && v.IsValid(ip, ver) { + return ip, ver + } + } + return nil, iputil.IPvUnknown +} + +func findForwardedFor(r *http.Request, v iputil.IPValidator) (net.IP, iputil.IPVersion) { + if value := r.Header.Get(xForwardedFor); value != "" { + for _, p := range strings.Split(value, ",") { + p = strings.TrimSpace(p) + if ip, ver := iputil.ParseIP(p); ip != nil && v.IsValid(ip, ver) { + return ip, ver + } + } + } + return nil, iputil.IPvUnknown +} + +func findRealIP(r *http.Request, v iputil.IPValidator) (net.IP, iputil.IPVersion) { + if value := r.Header.Get(xRealIP); value != "" { + value = strings.TrimSpace(value) + if ip, ver := iputil.ParseIP(value); ip != nil && v.IsValid(ip, ver) { + return ip, ver + } + } + return nil, iputil.IPvUnknown +} + +func findRemoteAddr(r *http.Request, v iputil.IPValidator) (net.IP, iputil.IPVersion) { + if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { + if ip, ver := iputil.ParseIP(host); ip != nil && v.IsValid(ip, ver) { + return ip, ver + } + } + return nil, iputil.IPvUnknown +} diff --git a/util/httputil/httputil_test.go b/util/httputil/httputil_test.go new file mode 100644 index 00000000000..f7166740fe5 --- /dev/null +++ b/util/httputil/httputil_test.go @@ -0,0 +1,327 @@ +package httputil + +import ( + "crypto/tls" + "net" + "net/http" + "testing" + + "github.com/prebid/prebid-server/util/iputil" + "github.com/stretchr/testify/assert" +) + +func TestIsSecure(t *testing.T) { + testCases := []struct { + description string + url string + xForwardedProto string + tls bool + expectIsSecure bool + }{ + { + description: "HTTP", + url: "http://host.com", + expectIsSecure: false, + }, + { + description: "HTTPS - Forwarded Protocol", + url: "http://host.com", + xForwardedProto: "https", + expectIsSecure: true, + }, + { + description: "HTTPS - Forwarded Protocol - Case Insensitive", + url: "http://host.com", + xForwardedProto: "HTTPS", + expectIsSecure: true, + }, + { + description: "HTTPS - Protocol", + url: "https://host.com", + expectIsSecure: true, + }, + { + description: "HTTPS - Protocol - Case Insensitive", + url: "HTTPS://host.com", + expectIsSecure: true, + }, + { + description: "HTTPS - TLS", + url: "http://host.com", + tls: true, + expectIsSecure: true, + }, + } + + for _, test := range testCases { + request, err := http.NewRequest("GET", test.url, nil) + if err != nil { + t.Fatalf("Unable to create test http request. Err: %v", err) + } + if test.xForwardedProto != "" { + request.Header.Add("X-Forwarded-Proto", test.xForwardedProto) + } + if test.tls { + request.TLS = &tls.ConnectionState{} + } + + result := IsSecure(request) + + assert.Equal(t, test.expectIsSecure, result, test.description) + } +} + +func TestFindIP(t *testing.T) { + alwaysTrue := hardcodedResponseIPValidator{response: true} + alwaysFalse := hardcodedResponseIPValidator{response: false} + + testCases := []struct { + description string + trueClientIP string + xForwardedFor string + xRealIP string + remoteAddr string + validator iputil.IPValidator + expectedIP net.IP + expectedVer iputil.IPVersion + }{ + { + description: "No Address", + expectedIP: nil, + expectedVer: iputil.IPvUnknown, + }, + { + description: "False Validator - IPv4", + trueClientIP: "1.1.1.1", + xForwardedFor: "2.2.2.2, 3.3.3.3", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysFalse, + expectedIP: nil, + expectedVer: iputil.IPvUnknown, + }, + { + description: "False Validator - IPv6", + trueClientIP: "1111::", + xForwardedFor: "2222::, 3333::", + xRealIP: "4444::", + remoteAddr: "[5555::]:5]", + validator: alwaysFalse, + expectedIP: nil, + expectedVer: iputil.IPvUnknown, + }, + { + description: "True Validator - IPv4 - True Client IP", + trueClientIP: "1.1.1.1", + xForwardedFor: "2.2.2.2, 3.3.3.3", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("1.1.1.1"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - True Client IP - Ignore Whitespace", + trueClientIP: " 1.1.1.1 ", + xForwardedFor: "2.2.2.2, 3.3.3.3", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("1.1.1.1"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - X Forwarded For", + trueClientIP: "", + xForwardedFor: "2.2.2.2, 3.3.3.3", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("2.2.2.2"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - X Forwarded For - Ignore Whitespace", + trueClientIP: "", + xForwardedFor: " 2.2.2.2, 3.3.3.3 ", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("2.2.2.2"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - X Real IP", + trueClientIP: "", + xForwardedFor: "", + xRealIP: "4.4.4.4", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("4.4.4.4"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - X Real IP - Ignore Whitespace", + trueClientIP: "", + xForwardedFor: "", + xRealIP: " 4.4.4.4 ", + remoteAddr: "5.5.5.5:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("4.4.4.4"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv4 - Remote Address", + trueClientIP: "", + xForwardedFor: "", + xRealIP: "", + remoteAddr: "5.5.5.5:80", + validator: alwaysTrue, + expectedIP: net.ParseIP("5.5.5.5"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - IPv6 - True Client IP", + trueClientIP: "1111::", + xForwardedFor: "2222::, 3333::", + xRealIP: "4444::", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("1111::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - True Client IP - Ignore Whitespace", + trueClientIP: " 1111:: ", + xForwardedFor: "2222::, 3333::", + xRealIP: "4444::", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("1111::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - X Forwarded For", + trueClientIP: "", + xForwardedFor: "2222::, 3333::", + xRealIP: "4444::", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("2222::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - X Forwarded For - Ignore Whitespace", + trueClientIP: "", + xForwardedFor: " 2222::, 3333:: ", + xRealIP: "4444::", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("2222::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - X Real IP", + trueClientIP: "", + xForwardedFor: "", + xRealIP: "4444::", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("4444::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - X Real IP - Ignore Whitespace", + trueClientIP: "", + xForwardedFor: "", + xRealIP: " 4444:: ", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("4444::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - IPv6 - Remote Address", + trueClientIP: "", + xForwardedFor: "", + xRealIP: "", + remoteAddr: "[5555::]:5", + validator: alwaysTrue, + expectedIP: net.ParseIP("5555::"), + expectedVer: iputil.IPv6, + }, + { + description: "True Validator - Malformed - All", + trueClientIP: "malformed", + xForwardedFor: "malformed", + xRealIP: "malformed", + remoteAddr: "malformed", + validator: alwaysTrue, + expectedIP: nil, + expectedVer: iputil.IPvUnknown, + }, + { + description: "True Validator - Malformed - Some", + trueClientIP: "malformed", + xForwardedFor: "malformed", + xRealIP: "4.4.4.4", + remoteAddr: "malformed", + validator: alwaysTrue, + expectedIP: net.ParseIP("4.4.4.4"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - Malformed - X Forwarded For - IPv4", + trueClientIP: "malformed", + xForwardedFor: "malformed, 4.4.4.4, 3333::, malformed", + xRealIP: "malformed", + remoteAddr: "malformed", + validator: alwaysTrue, + expectedIP: net.ParseIP("4.4.4.4"), + expectedVer: iputil.IPv4, + }, + { + description: "True Validator - Malformed - X Forwarded For - IPv6", + trueClientIP: "malformed", + xForwardedFor: "malformed, 3333::, 4.4.4.4, malformed", + xRealIP: "malformed", + remoteAddr: "malformed", + validator: alwaysTrue, + expectedIP: net.ParseIP("3333::"), + expectedVer: iputil.IPv6, + }, + } + + for _, test := range testCases { + // Build Request + request, err := http.NewRequest("GET", "http://anyurl.com", nil) + if err != nil { + t.Fatalf("Unable to create test http request. Err: %v", err) + } + if test.trueClientIP != "" { + request.Header.Add("True-Client-IP", test.trueClientIP) + } + if test.xForwardedFor != "" { + request.Header.Add("X-Forwarded-For", test.xForwardedFor) + } + if test.xRealIP != "" { + request.Header.Add("X-Real-IP", test.xRealIP) + } + request.RemoteAddr = test.remoteAddr + + // Run Test + ip, ver := FindIP(request, test.validator) + + // Assertions + assert.Equal(t, test.expectedIP, ip, test.description+":ip") + assert.Equal(t, test.expectedVer, ver, test.description+":ver") + } +} + +type hardcodedResponseIPValidator struct { + response bool +} + +func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool { + return v.response +} diff --git a/util/iputil/parse.go b/util/iputil/parse.go new file mode 100644 index 00000000000..bcb00760e22 --- /dev/null +++ b/util/iputil/parse.go @@ -0,0 +1,27 @@ +package iputil + +import ( + "net" + "strings" +) + +// IPVersion is the numerical version of the IP address spec (4 or 6). +type IPVersion int + +// IP address versions. +const ( + IPvUnknown IPVersion = 0 + IPv4 IPVersion = 4 + IPv6 IPVersion = 6 +) + +// ParseIP parses v as an ip address returning the result and version, or nil and unknown if invalid. +func ParseIP(v string) (net.IP, IPVersion) { + if ip := net.ParseIP(v); ip != nil { + if strings.ContainsRune(v, ':') { + return ip, IPv6 + } + return ip, IPv4 + } + return nil, IPvUnknown +} diff --git a/util/iputil/parse_test.go b/util/iputil/parse_test.go new file mode 100644 index 00000000000..53431b0f2a9 --- /dev/null +++ b/util/iputil/parse_test.go @@ -0,0 +1,30 @@ +package iputil + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseIP(t *testing.T) { + testCases := []struct { + input string + expectedVer IPVersion + expectedIP net.IP + }{ + {"", IPvUnknown, nil}, + {"1.1.1.1", IPv4, net.IPv4(1, 1, 1, 1)}, + {"-1.-1.-1.-1", IPvUnknown, nil}, + {"256.256.256.256", IPvUnknown, nil}, + {"::ffff:1.1.1.1", IPv6, net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 1, 1, 1, 1}}, + {"0101::", IPv6, net.IP{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {"zzzz::", IPvUnknown, nil}, + } + + for _, test := range testCases { + ip, ver := ParseIP(test.input) + assert.Equal(t, test.expectedVer, ver) + assert.Equal(t, test.expectedIP, ip) + } +} diff --git a/util/iputil/validator.go b/util/iputil/validator.go new file mode 100644 index 00000000000..e4b822f0c7c --- /dev/null +++ b/util/iputil/validator.go @@ -0,0 +1,48 @@ +package iputil + +import ( + "net" +) + +// IPValidator is the interface for validating an ip address and version. +type IPValidator interface { + // IsValid returns true when an IP address is determined to be valid. + IsValid(net.IP, IPVersion) bool +} + +// PublicNetworkIPValidator validates an ip address which is not contained in the list of known private networks. +type PublicNetworkIPValidator struct { + IPv4PrivateNetworks []net.IPNet + IPv6PrivateNetworks []net.IPNet +} + +// IsValid implements the IPValidator interface. +func (v PublicNetworkIPValidator) IsValid(ip net.IP, ver IPVersion) bool { + var privateNetworks []net.IPNet + switch ver { + case IPv4: + privateNetworks = v.IPv4PrivateNetworks + case IPv6: + privateNetworks = v.IPv6PrivateNetworks + default: + return false + } + + for _, ipNet := range privateNetworks { + if ipNet.Contains(ip) { + return false + } + } + + return true +} + +// VersionIPValidator validates an ip address based on the desired ip version. +type VersionIPValidator struct { + Version IPVersion +} + +// IsValid implements the IPValidator interface. +func (v VersionIPValidator) IsValid(ip net.IP, ver IPVersion) bool { + return ver == v.Version +} diff --git a/util/iputil/validator_test.go b/util/iputil/validator_test.go new file mode 100644 index 00000000000..4419af22c04 --- /dev/null +++ b/util/iputil/validator_test.go @@ -0,0 +1,222 @@ +package iputil + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPublicNetworkIPValidator(t *testing.T) { + ipv4Network1 := net.IPNet{IP: net.ParseIP("1.0.0.0"), Mask: net.CIDRMask(8, 32)} + ipv4Network2 := net.IPNet{IP: net.ParseIP("2.0.0.0"), Mask: net.CIDRMask(8, 32)} + + ipv6Network1 := net.IPNet{IP: net.ParseIP("3300::"), Mask: net.CIDRMask(8, 128)} + ipv6Network2 := net.IPNet{IP: net.ParseIP("4400::"), Mask: net.CIDRMask(8, 128)} + + testCases := []struct { + description string + ip net.IP + ver IPVersion + ipv4PrivateNetworks []net.IPNet + ipv6PrivateNetworks []net.IPNet + expected bool + }{ + { + description: "IPv4 - Public - None", + ip: net.ParseIP("1.1.1.1"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: true, + }, + { + description: "IPv4 - Public - One", + ip: net.ParseIP("2.2.2.2"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: true, + }, + { + description: "IPv4 - Public - Many", + ip: net.ParseIP("3.3.3.3"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network2}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: true, + }, + { + description: "IPv4 - Private - One", + ip: net.ParseIP("1.1.1.1"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: false, + }, + { + description: "IPv4 - Private - Many", + ip: net.ParseIP("2.2.2.2"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network2}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: false, + }, + { + description: "IPv6 - Public - None", + ip: net.ParseIP("3333::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{}, + expected: true, + }, + { + description: "IPv6 - Public - One", + ip: net.ParseIP("4444::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1}, + expected: true, + }, + { + description: "IPv6 - Public - Many", + ip: net.ParseIP("5555::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: true, + }, + { + description: "IPv6 - Private - One", + ip: net.ParseIP("3333::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1}, + expected: false, + }, + { + description: "IPv6 - Private - Many", + ip: net.ParseIP("4444::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: false, + }, + { + description: "Mixed - Unknown", + ip: net.ParseIP("3.3.3.3"), + ver: IPvUnknown, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: false, + }, + { + description: "Mixed - Public - IPv4", + ip: net.ParseIP("3.3.3.3"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: true, + }, + { + description: "Mixed - Public - IPv6", + ip: net.ParseIP("5555::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: true, + }, + { + description: "Mixed - Private - IPv4", + ip: net.ParseIP("1.1.1.1"), + ver: IPv4, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: false, + }, + { + description: "Mixed - Private - IPv6", + ip: net.ParseIP("3333::"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{ipv4Network1, ipv4Network1}, + ipv6PrivateNetworks: []net.IPNet{ipv6Network1, ipv6Network2}, + expected: false, + }, + { + description: "Mixed - Public - IPv6 Encoded IPv4", + ip: net.ParseIP("::FFFF:1.1.1.1"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{{IP: net.ParseIP("1.0.0.0"), Mask: net.CIDRMask(8, 32)}}, + ipv6PrivateNetworks: []net.IPNet{{IP: net.ParseIP("::FFFF:2.0.0.0"), Mask: net.CIDRMask(108, 128)}}, + expected: true, + }, + { + description: "Mixed - Private - IPv6 Encoded IPv4", + ip: net.ParseIP("::FFFF:2.2.2.2"), + ver: IPv6, + ipv4PrivateNetworks: []net.IPNet{{IP: net.ParseIP("1.0.0.0"), Mask: net.CIDRMask(8, 32)}}, + ipv6PrivateNetworks: []net.IPNet{{IP: net.ParseIP("::FFFF:2.0.0.0"), Mask: net.CIDRMask(108, 128)}}, + expected: false, + }, + } + + for _, test := range testCases { + requestValidation := PublicNetworkIPValidator{ + IPv4PrivateNetworks: test.ipv4PrivateNetworks, + IPv6PrivateNetworks: test.ipv6PrivateNetworks, + } + + result := requestValidation.IsValid(test.ip, test.ver) + + assert.Equal(t, test.expected, result, test.description) + } +} + +func TestVersionIPValidator(t *testing.T) { + testCases := []struct { + description string + validatorVersion IPVersion + ip net.IP + ipVer IPVersion + expected bool + }{ + { + description: "IPv4", + validatorVersion: IPv4, + ip: net.ParseIP("1.1.1.1"), + ipVer: IPv4, + expected: true, + }, + { + description: "IPv4 - Given Unknown", + validatorVersion: IPv4, + ip: nil, + ipVer: IPvUnknown, + expected: false, + }, + { + description: "IPv6", + validatorVersion: IPv6, + ip: net.ParseIP("1111::"), + ipVer: IPv6, + expected: true, + }, + { + description: "IPv6 - Given Unknown", + validatorVersion: IPv6, + ip: nil, + ipVer: IPvUnknown, + expected: false, + }, + } + + for _, test := range testCases { + m := VersionIPValidator{ + Version: test.validatorVersion, + } + + result := m.IsValid(test.ip, test.ipVer) + + assert.Equal(t, test.expected, result) + } +} From 9b96f50afeb81a665668525ff1804d04c3b64ea2 Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Mon, 29 Jun 2020 22:45:43 +0300 Subject: [PATCH 128/318] Change endpont address (#1370) * Adman adapter * add adman line to syner test * add tests * fix issues * fix web banner test * add 404 banner * fmt * rase coverage * del redundant files * change endpont address * change config endpoint Co-authored-by: Aiholkin --- adapters/adman/adman_test.go | 2 +- adapters/adman/admantest/exemplary/simple-banner.json | 6 +++--- adapters/adman/admantest/exemplary/simple-video.json | 2 +- adapters/adman/admantest/exemplary/simple-web-banner.json | 6 +++--- adapters/adman/admantest/supplemental/bad_response.json | 2 +- adapters/adman/admantest/supplemental/status-204.json | 2 +- adapters/adman/admantest/supplemental/status-404.json | 2 +- config/config.go | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/adapters/adman/adman_test.go b/adapters/adman/adman_test.go index da0f37e9a48..4ef88933759 100644 --- a/adapters/adman/adman_test.go +++ b/adapters/adman/adman_test.go @@ -7,6 +7,6 @@ import ( ) func TestJsonSamples(t *testing.T) { - admanAdapter := NewAdmanBidder("http://eu-ams-1.admanmedia.com/?c=o&m=ortb") + admanAdapter := NewAdmanBidder("http://pub.admanmedia.com/?c=o&m=ortb") adapterstest.RunJSONBidderTest(t, "admantest", admanAdapter) } diff --git a/adapters/adman/admantest/exemplary/simple-banner.json b/adapters/adman/admantest/exemplary/simple-banner.json index 41f76e00645..8bbe16aa0fe 100644 --- a/adapters/adman/admantest/exemplary/simple-banner.json +++ b/adapters/adman/admantest/exemplary/simple-banner.json @@ -37,7 +37,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "imp": [ @@ -84,7 +84,7 @@ "id": "test_bid_id", "impid": "test-imp-id", "price": 0.27543, - "adm": "", + "adm": "", "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", @@ -114,7 +114,7 @@ "id": "test_bid_id", "impid": "test-imp-id", "price": 0.27543, - "adm": "", + "adm": "", "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", diff --git a/adapters/adman/admantest/exemplary/simple-video.json b/adapters/adman/admantest/exemplary/simple-video.json index d7fa82d274d..159a30a93e0 100644 --- a/adapters/adman/admantest/exemplary/simple-video.json +++ b/adapters/adman/admantest/exemplary/simple-video.json @@ -30,7 +30,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "device": { diff --git a/adapters/adman/admantest/exemplary/simple-web-banner.json b/adapters/adman/admantest/exemplary/simple-web-banner.json index ce872bff52b..0ceaac7c6d5 100644 --- a/adapters/adman/admantest/exemplary/simple-web-banner.json +++ b/adapters/adman/admantest/exemplary/simple-web-banner.json @@ -36,7 +36,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "imp": [ @@ -82,7 +82,7 @@ "id": "test_bid_id", "impid": "test-imp-id", "price": 0.27543, - "adm": "", + "adm": "", "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", @@ -112,7 +112,7 @@ "id": "test_bid_id", "impid": "test-imp-id", "price": 0.27543, - "adm": "", + "adm": "", "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", diff --git a/adapters/adman/admantest/supplemental/bad_response.json b/adapters/adman/admantest/supplemental/bad_response.json index 8c349297e73..d5a28c74256 100644 --- a/adapters/adman/admantest/supplemental/bad_response.json +++ b/adapters/adman/admantest/supplemental/bad_response.json @@ -35,7 +35,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adman/admantest/supplemental/status-204.json b/adapters/adman/admantest/supplemental/status-204.json index 7f9a12dec29..72b28bffdcf 100644 --- a/adapters/adman/admantest/supplemental/status-204.json +++ b/adapters/adman/admantest/supplemental/status-204.json @@ -35,7 +35,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/adman/admantest/supplemental/status-404.json b/adapters/adman/admantest/supplemental/status-404.json index 560878342f0..043afbdc1dc 100644 --- a/adapters/adman/admantest/supplemental/status-404.json +++ b/adapters/adman/admantest/supplemental/status-404.json @@ -35,7 +35,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://eu-ams-1.admanmedia.com/?c=o&m=ortb", + "uri": "http://pub.admanmedia.com/?c=o&m=ortb", "body": { "id": "test-request-id", "imp": [ diff --git a/config/config.go b/config/config.go index 50cfbb1c170..16bab2996be 100755 --- a/config/config.go +++ b/config/config.go @@ -773,7 +773,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json") v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") v.SetDefault("adapters.adkerneladn.endpoint", "http://{{.Host}}/rtbpub?account={{.PublisherID}}") - v.SetDefault("adapters.adman.endpoint", "http://eu-ams-1.admanmedia.com/?c=o&m=ortb") + v.SetDefault("adapters.adman.endpoint", "http://pub.admanmedia.com/?c=o&m=ortb") v.SetDefault("adapters.admixer.endpoint", "http://inv-nets.admixer.net/pbs.aspx") v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") From 919d29ac3a6a29a55baa392d0dbbb88872ddd3c4 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Tue, 30 Jun 2020 16:29:43 +0200 Subject: [PATCH 129/318] Don't override test parameter (#1373) --- adapters/pubnative/pubnative.go | 1 - adapters/pubnative/pubnativetest/exemplary/simple-banner.json | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/pubnative/pubnative.go b/adapters/pubnative/pubnative.go index 4dc92920d8e..777ac4a05ed 100644 --- a/adapters/pubnative/pubnative.go +++ b/adapters/pubnative/pubnative.go @@ -83,7 +83,6 @@ func checkRequest(request *openrtb.BidRequest) error { } } - request.Test = 0 // don't forward test flag to PN adserver return nil } diff --git a/adapters/pubnative/pubnativetest/exemplary/simple-banner.json b/adapters/pubnative/pubnativetest/exemplary/simple-banner.json index 7c7d1319a50..5297cd3284d 100644 --- a/adapters/pubnative/pubnativetest/exemplary/simple-banner.json +++ b/adapters/pubnative/pubnativetest/exemplary/simple-banner.json @@ -111,6 +111,7 @@ }, "at": 1, "tmax": 200, + "test": 1, "source": { "tid": "283746293874293" }, From e430c629b140d263f0c38195862fae5661bb785f Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Tue, 30 Jun 2020 12:44:01 -0400 Subject: [PATCH 130/318] OpenX + Facebook Hardening (#1368) --- adapters/adapterstest/test_json.go | 6 +- .../exemplary/banner-app.json | 116 +++++++++++++++ .../{banner.json => banner-site.json} | 6 - .../exemplary/interstitial.json | 6 - .../exemplary/native-1.1.json | 6 - .../audienceNetworktest/exemplary/video.json | 6 - .../supplemental/banner-format-only.json | 6 - .../supplemental/invalid-adm.json | 103 ++++++++++++++ .../supplemental/invalid-interstitial.json | 40 ++++++ .../supplemental/missing-adm-bidid.json | 107 ++++++++++++++ .../supplemental/missing-adm.json | 106 ++++++++++++++ .../supplemental/multi-imp.json | 12 -- .../supplemental/no-bid-204.json | 6 - .../supplemental/no-imps.json | 22 +++ .../supplemental/server-error-500.json | 87 ++++++++++++ .../supplemental/split-placementId.json | 6 - adapters/audienceNetwork/facebook.go | 132 +++++++++--------- adapters/audienceNetwork/facebook_test.go | 31 +++- adapters/openx/openx.go | 6 +- exchange/adapter_map.go | 1 - util/maputil/maputil.go | 21 +++ util/maputil/maputil_test.go | 113 +++++++++++++++ 22 files changed, 813 insertions(+), 132 deletions(-) create mode 100644 adapters/audienceNetwork/audienceNetworktest/exemplary/banner-app.json rename adapters/audienceNetwork/audienceNetworktest/exemplary/{banner.json => banner-site.json} (96%) create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json create mode 100644 util/maputil/maputil.go create mode 100644 util/maputil/maputil_test.go diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go index 8fdb9c5d9d6..a25a4f1905a 100644 --- a/adapters/adapterstest/test_json.go +++ b/adapters/adapterstest/test_json.go @@ -164,14 +164,16 @@ type httpRequest struct { } type httpResponse struct { - Status int `json:"status"` - Body json.RawMessage `json:"body"` + Status int `json:"status"` + Body json.RawMessage `json:"body"` + Headers http.Header `json:"headers"` } func (resp *httpResponse) ToResponseData(t *testing.T) *adapters.ResponseData { return &adapters.ResponseData{ StatusCode: resp.Status, Body: resp.Body, + Headers: resp.Headers, } } diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-app.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-app.json new file mode 100644 index 00000000000..3ac62d90cd4 --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-app.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "app": { + "id": "app-abc", + "bundle": "com.prebid" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [{ + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-imp-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": -1, + "h": 250 + }, + "tagid": "123_456" + }], + "app": { + "id": "app-abc", + "bundle": "com.prebid", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-imp-id", + "seatbid": [{ + "bid": [{ + "id": "987", + "impid": "test-imp-id", + "price": 1.000000, + "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}", + "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" + }] + }], + "bidid": "654", + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [{ + "bid": { + "id": "987", + "impid": "test-imp-id", + "price": 1, + "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}", + "adid": "987", + "crid": "987", + "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json similarity index 96% rename from adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json rename to adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json index f5f92515e26..01bab3dfd71 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json @@ -62,12 +62,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json index bad228d5f18..9f563f11948 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json @@ -64,12 +64,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json index 9090d80d099..16bed344767 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json @@ -56,12 +56,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json index 22c62f8b821..5ece0f08530 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json @@ -66,12 +66,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json index 3edd6569258..5469fefbd65 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json @@ -64,12 +64,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json new file mode 100644 index 00000000000..f145f5fe4ce --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [{ + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-imp-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": -1, + "h": 250 + }, + "tagid": "123_456" + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-imp-id", + "seatbid": [{ + "bid": [{ + "id": "987", + "impid": "test-imp-id", + "price": 1.000000, + "adm": "malformed", + "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" + }] + }], + "bidid": "654", + "cur": "USD" + } + } + }], + "expectedMakeBidsErrors": [{ + "value": "invalid character 'm' looking for beginning of value", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json new file mode 100644 index 00000000000..ad19d94c6e9 --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json @@ -0,0 +1,40 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "minduration": 15, + "maxduration": 30, + "protocols": [2, 3, 5, 6, 7, 8], + "linearity": 1, + "w": 940, + "h": 560 + }, + "instl": 1, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "expectedMakeRequestsErrors": [{ + "value": "imp #test-imp-id: interstitial imps are only supported for banner", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json new file mode 100644 index 00000000000..b57c900104e --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json @@ -0,0 +1,107 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [{ + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-imp-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": -1, + "h": 250 + }, + "tagid": "123_456" + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-imp-id", + "seatbid": [{ + "bid": [{ + "id": "987", + "impid": "test-imp-id", + "price": 1.000000, + "adm": "{\"type\":\"ID\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}", + "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" + }] + }], + "bidid": "654", + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [] + }], + "expectedMakeBidsErrors": [{ + "value": "bid 987 missing 'bid_id' in 'adm'", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json new file mode 100644 index 00000000000..23227aab959 --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [{ + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-imp-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": -1, + "h": 250 + }, + "tagid": "123_456" + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-imp-id", + "seatbid": [{ + "bid": [{ + "id": "987", + "impid": "test-imp-id", + "price": 1.000000, + "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", + "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" + }] + }], + "bidid": "654", + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [] + }], + "expectedMakeBidsErrors": [{ + "value": "Bid 987 missing 'adm'", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json index 16e8aede10c..231c2826548 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json @@ -81,12 +81,6 @@ "tagid": "pub1_plmt1" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", @@ -158,12 +152,6 @@ "tagid": "pub2_plmt2" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json index bb192aad76f..45b35e05dd9 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json @@ -56,12 +56,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json new file mode 100644 index 00000000000..7420f7e8fb2 --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json @@ -0,0 +1,22 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "expectedMakeRequestsErrors": [{ + "value": "No impressions provided", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json new file mode 100644 index 00000000000..7ff8886139a --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json @@ -0,0 +1,87 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}", + "ver": "1.1" + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "httpcalls": [{ + "expectedRequest": { + "uri": "https://an.facebook.com/placementbid.ortb", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Fb-Pool-Routing-Token": [ + "v4_bidder_token" + ] + }, + "body": { + "id": "test-imp-id", + "imp": [{ + "id": "test-imp-id", + "native": { + "w": -1, + "h": -1 + }, + "tagid": "123_456" + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org", + "publisher": { + "id": "123" + } + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500, + "ext": { + "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", + "platformid": "test-platform-id" + } + } + }, + "mockResponse": { + "headers": { + "X-Fb-An-Errors": [ + "someError" + ]}, + "status": 500 + } + }], + "expectedMakeBidsErrors": [{ + "value": "Unexpected status code 500 with error message 'someError'", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json index 4c561c55276..34c1eccc58e 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json @@ -50,12 +50,6 @@ "tagid": "123_456" } ], - "ext": { - "appnexus": { - "hb_source": 5 - }, - "prebid": {} - }, "site": { "domain": "prebid.org", "page": "prebid.org", diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index 9edb9a7d57e..f4091e4e23c 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -10,16 +10,17 @@ import ( "net/http" "strings" - "github.com/buger/jsonparser" - "github.com/golang/glog" - "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/util/maputil" + + "github.com/buger/jsonparser" + "github.com/golang/glog" + "github.com/mxmCherry/openrtb" ) type FacebookAdapter struct { - http *adapters.HTTPAdapter URI string nonSecureUri string platformID string @@ -35,15 +36,6 @@ var supportedBannerHeights = map[uint64]bool{ 250: true, } -// used for cookies and such -func (a *FacebookAdapter) Name() string { - return "audienceNetwork" -} - -func (a *FacebookAdapter) SkipNoCookies() bool { - return false -} - type facebookReqExt struct { PlatformID string `json:"platformid"` AuthID string `json:"authentication_id"` @@ -178,8 +170,10 @@ func (this *FacebookAdapter) modifyImp(out *openrtb.Imp) error { } } - switch impType { - case openrtb_ext.BidTypeBanner: + if impType == openrtb_ext.BidTypeBanner { + bannerCopy := *out.Banner + out.Banner = &bannerCopy + if out.Instl == 1 { out.Banner.W = openrtb.Uint64Ptr(0) out.Banner.H = openrtb.Uint64Ptr(0) @@ -212,7 +206,6 @@ func (this *FacebookAdapter) modifyImp(out *openrtb.Imp) error { /* This will get overwritten post-serialization */ out.Banner.W = openrtb.Uint64Ptr(0) out.Banner.Format = nil - break } return nil @@ -239,102 +232,106 @@ func (this *FacebookAdapter) extractPlacementAndPublisher(out *openrtb.Imp) (str } } - placementId := fbExt.PlacementId - publisherId := fbExt.PublisherId + placementID := fbExt.PlacementId + publisherID := fbExt.PublisherId // Support the legacy path with the caller was expected to pass in just placementId // which was an underscore concantenated string with the publisherId and placementId. // The new path for callers is to pass in the placementId and publisherId independently // and the below code will prefix the placementId that we pass to FAN with the publsiherId // so that we can abstract the implementation details from the caller - toks := strings.Split(placementId, "_") + toks := strings.Split(placementID, "_") if len(toks) == 1 { - if publisherId == "" { + if publisherID == "" { return "", "", &errortypes.BadInput{ Message: "Missing publisherId param", } } - return placementId, publisherId, nil + return placementID, publisherID, nil } else if len(toks) == 2 { - publisherId = toks[0] - placementId = toks[1] + publisherID = toks[0] + placementID = toks[1] } else { return "", "", &errortypes.BadInput{ - Message: fmt.Sprintf("Invalid placementId param '%s' and publisherId param '%s'", placementId, publisherId), + Message: fmt.Sprintf("Invalid placementId param '%s' and publisherId param '%s'", placementID, publisherID), } } - return placementId, publisherId, nil + return placementID, publisherID, nil } // XXX: This entire function is just a hack to get around mxmCherry 11.0.0 limitations, without // having to fork the library and maintain our own branch -func modifyImpCustom(json []byte, imp *openrtb.Imp) ([]byte, error) { +func modifyImpCustom(jsonData []byte, imp *openrtb.Imp) ([]byte, error) { impType, ok := resolveImpType(imp) if ok == false { panic("processing an invalid impression") } - var err error + var jsonMap map[string]interface{} + err := json.Unmarshal(jsonData, &jsonMap) + if err != nil { + return jsonData, err + } + + var impMap map[string]interface{} + if impSlice, ok := maputil.ReadEmbeddedSlice(jsonMap, "imp"); !ok { + return jsonData, errors.New("unable to find imp in json data") + } else if len(impSlice) == 0 { + return jsonData, errors.New("unable to find imp[0] in json data") + } else if impMap, ok = impSlice[0].(map[string]interface{}); !ok { + return jsonData, errors.New("unexpected type for imp[0] found in json data") + } switch impType { case openrtb_ext.BidTypeBanner: - // The current version of mxmCherry (11.0.0) repesents banner.w as unsigned - // integers, so setting a value of -1 is not possible which is why we have to do it + // The current version of mxmCherry (11.0.0) represents banner.w as an unsigned + // integer, so setting a value of -1 is not possible which is why we have to do it // post-serialization - - // The above does not apply to interstitial impressions - if imp.Instl == 1 { - break - } - - json, err = jsonparser.Set(json, []byte("-1"), "imp", "[0]", "banner", "w") - if err != nil { - return json, err + isInterstitial := imp.Instl == 1 + if !isInterstitial { + if bannerMap, ok := maputil.ReadEmbeddedMap(impMap, "banner"); ok { + bannerMap["w"] = json.RawMessage("-1") + } else { + return jsonData, errors.New("unable to find imp[0].banner in json data") + } } - break - case openrtb_ext.BidTypeVideo: // mxmCherry omits video.w/h if set to zero, so we need to force set those // fields to zero post-serialization for the time being - json, err = jsonparser.Set(json, []byte("0"), "imp", "[0]", "video", "w") - if err != nil { - return json, err + if videoMap, ok := maputil.ReadEmbeddedMap(impMap, "video"); ok { + videoMap["w"] = json.RawMessage("0") + videoMap["h"] = json.RawMessage("0") + } else { + return jsonData, errors.New("unable to find imp[0].video in json data") } - json, err = jsonparser.Set(json, []byte("0"), "imp", "[0]", "video", "h") - if err != nil { - return json, err + case openrtb_ext.BidTypeNative: + nativeMap, ok := maputil.ReadEmbeddedMap(impMap, "native") + if !ok { + return jsonData, errors.New("unable to find imp[0].video in json data") } - break - - case openrtb_ext.BidTypeNative: // Set w/h to -1 for native impressions based on the facebook native spec. // We have to set this post-serialization since the OpenRTB protocol doesn't - // actaully support w/h in the native object - json, err = jsonparser.Set(json, []byte("-1"), "imp", "[0]", "native", "w") - if err != nil { - return json, err - } - - json, err = jsonparser.Set(json, []byte("-1"), "imp", "[0]", "native", "h") - if err != nil { - return json, err - } + // actually support w/h in the native object + nativeMap["w"] = json.RawMessage("-1") + nativeMap["h"] = json.RawMessage("-1") // The FAN adserver does not expect the native request payload, all that information // is derived server side based on the placement ID. We need to remove these pieces of // information manually since OpenRTB (and thus mxmCherry) never omit native.request - json = jsonparser.Delete(json, "imp", "[0]", "native", "ver") - json = jsonparser.Delete(json, "imp", "[0]", "native", "request") - - break + delete(nativeMap, "ver") + delete(nativeMap, "request") } - return json, nil + if jsonReEncoded, err := json.Marshal(jsonMap); err == nil { + return jsonReEncoded, nil + } else { + return nil, fmt.Errorf("unable to encode json data (%v)", err) + } } func (this *FacebookAdapter) MakeBids(request *openrtb.BidRequest, adapterRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { @@ -430,7 +427,7 @@ func resolveImpType(imp *openrtb.Imp) (openrtb_ext.BidType, bool) { return openrtb_ext.BidTypeBanner, false } -func NewFacebookBidder(client *http.Client, platformID string, appSecret string) adapters.Bidder { +func NewFacebookBidder(platformID string, appSecret string) adapters.Bidder { if platformID == "" { glog.Errorf("No facebook partnerID specified. Calls to the Audience Network will fail. Did you set adapters.facebook.platform_id in the app config?") return &adapters.MisconfiguredBidder{ @@ -447,11 +444,8 @@ func NewFacebookBidder(client *http.Client, platformID string, appSecret string) } } - a := &adapters.HTTPAdapter{Client: client} - return &FacebookAdapter{ - http: a, - URI: "https://an.facebook.com/placementbid.ortb", + URI: "https://an.facebook.com/placementbid.ortb", //for AB test nonSecureUri: "http://an.facebook.com/placementbid.ortb", platformID: platformID, diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go index 784a540e596..7f567d6137b 100644 --- a/adapters/audienceNetwork/facebook_test.go +++ b/adapters/audienceNetwork/facebook_test.go @@ -1,6 +1,7 @@ package audienceNetwork import ( + "errors" "testing" "time" @@ -40,14 +41,14 @@ type FacebookExt struct { } func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder(nil, "test-platform-id", "test-app-secret")) + adapterstest.RunJSONBidderTest(t, "audienceNetworktest", NewFacebookBidder("test-platform-id", "test-app-secret")) } func TestMakeTimeoutNoticeApp(t *testing.T) { req := adapters.RequestData{ Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"app":{"publisher":{"id":"5678"}}}`), } - fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + fba := NewFacebookBidder("test-platform-id", "test-app-secret") tb, ok := fba.(adapters.TimeoutBidder) if !ok { @@ -64,7 +65,7 @@ func TestMakeTimeoutNoticeSite(t *testing.T) { req := adapters.RequestData{ Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"site":{"publisher":{"id":"5678"}}}`), } - fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + fba := NewFacebookBidder("test-platform-id", "test-app-secret") tb, ok := fba.(adapters.TimeoutBidder) if !ok { @@ -81,7 +82,7 @@ func TestMakeTimeoutNoticeBadRequest(t *testing.T) { req := adapters.RequestData{ Body: []byte(`{"imp":[{{"id":"1234"}}`), } - fba := NewFacebookBidder(nil, "test-platform-id", "test-app-secret") + fba := NewFacebookBidder("test-platform-id", "test-app-secret") tb, ok := fba.(adapters.TimeoutBidder) if !ok { @@ -93,3 +94,25 @@ func TestMakeTimeoutNoticeBadRequest(t *testing.T) { assert.NotNil(t, err, "Facebook MakeTimeoutNotification() did not return an error") } + +func TestNewFacebookBidderMissingPlatformID(t *testing.T) { + result := NewFacebookBidder("", "anyAppSecret") + + expected := &adapters.MisconfiguredBidder{ + Name: "audienceNetwork", + Error: errors.New("Audience Network is not configured properly on this Prebid Server deploy. If you believe this should work, contact the company hosting the service and tell them to check their configuration."), + } + + assert.Equal(t, expected, result) +} + +func TestNewFacebookBidderMissingAppSecret(t *testing.T) { + result := NewFacebookBidder("anyPlatformID", "") + + expected := &adapters.MisconfiguredBidder{ + Name: "audienceNetwork", + Error: errors.New("Audience Network is not configured properly on this Prebid Server deploy. If you believe this should work, contact the company hosting the service and tell them to check their configuration."), + } + + assert.Equal(t, expected, result) +} diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go index 63297d0a4ee..ca88b18bdb8 100644 --- a/adapters/openx/openx.go +++ b/adapters/openx/openx.go @@ -143,11 +143,13 @@ func preprocess(imp *openrtb.Imp, reqExt *openxReqExt) error { } if imp.Video != nil { + videoCopy := *imp.Video if bidderExt.Prebid != nil && bidderExt.Prebid.IsRewardedInventory == 1 { - imp.Video.Ext = json.RawMessage(`{"rewarded":1}`) + videoCopy.Ext = json.RawMessage(`{"rewarded":1}`) } else { - imp.Video.Ext = nil + videoCopy.Ext = nil } + imp.Video = &videoCopy } return nil diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index c30bb0c622e..1f62d232233 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -122,7 +122,6 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), openrtb_ext.BidderFacebook: audienceNetwork.NewFacebookBidder( - client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret), openrtb_ext.BidderGamma: gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint), diff --git a/util/maputil/maputil.go b/util/maputil/maputil.go new file mode 100644 index 00000000000..0d1d7dbb51c --- /dev/null +++ b/util/maputil/maputil.go @@ -0,0 +1,21 @@ +package maputil + +// ReadEmbeddedMap reads element k from the map m as a map[string]interface{}. +func ReadEmbeddedMap(m map[string]interface{}, k string) (map[string]interface{}, bool) { + if v, ok := m[k]; ok { + vCasted, ok := v.(map[string]interface{}) + return vCasted, ok + } + + return nil, false +} + +// ReadEmbeddedSlice reads element k from the map m as a []interface{}. +func ReadEmbeddedSlice(m map[string]interface{}, k string) ([]interface{}, bool) { + if v, ok := m[k]; ok { + vCasted, ok := v.([]interface{}) + return vCasted, ok + } + + return nil, false +} diff --git a/util/maputil/maputil_test.go b/util/maputil/maputil_test.go new file mode 100644 index 00000000000..2e6955cec9b --- /dev/null +++ b/util/maputil/maputil_test.go @@ -0,0 +1,113 @@ +package maputil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadEmbeddedMap(t *testing.T) { + testCases := []struct { + description string + value map[string]interface{} + key string + expectedMap map[string]interface{} + expectedOK bool + }{ + { + description: "Nil", + value: nil, + key: "", + expectedMap: nil, + expectedOK: false, + }, + { + description: "Empty", + value: map[string]interface{}{}, + key: "foo", + expectedMap: nil, + expectedOK: false, + }, + { + description: "Success", + value: map[string]interface{}{"foo": map[string]interface{}{"bar": 42}}, + key: "foo", + expectedMap: map[string]interface{}{"bar": 42}, + expectedOK: true, + }, + { + description: "Not Found", + value: map[string]interface{}{"foo": map[string]interface{}{"bar": 42}}, + key: "notFound", + expectedMap: nil, + expectedOK: false, + }, + { + description: "Wrong Type", + value: map[string]interface{}{"foo": 42}, + key: "foo", + expectedMap: nil, + expectedOK: false, + }, + } + + for _, test := range testCases { + resultMap, resultOK := ReadEmbeddedMap(test.value, test.key) + + assert.Equal(t, test.expectedMap, resultMap, test.description+":map") + assert.Equal(t, test.expectedOK, resultOK, test.description+":ok") + } +} + +func TestReadEmbeddedSlice(t *testing.T) { + testCases := []struct { + description string + value map[string]interface{} + key string + expectedSlice []interface{} + expectedOK bool + }{ + { + description: "Nil", + value: nil, + key: "", + expectedSlice: nil, + expectedOK: false, + }, + { + description: "Empty", + value: map[string]interface{}{}, + key: "foo", + expectedSlice: nil, + expectedOK: false, + }, + { + description: "Success", + value: map[string]interface{}{"foo": []interface{}{42}}, + key: "foo", + expectedSlice: []interface{}{42}, + expectedOK: true, + }, + { + description: "Not Found", + value: map[string]interface{}{"foo": []interface{}{42}}, + key: "notFound", + expectedSlice: nil, + expectedOK: false, + }, + { + description: "Wrong Type", + value: map[string]interface{}{"foo": 42}, + key: "foo", + expectedSlice: nil, + expectedOK: false, + }, + } + + for _, test := range testCases { + resultSlice, resultOK := ReadEmbeddedSlice(test.value, test.key) + + assert.Equal(t, test.expectedSlice, resultSlice, test.description+":slicd") + assert.Equal(t, test.expectedOK, resultOK, test.description+":ok") + } +} From 74af63b88166afd5bdcb2f388908a1f908855ff1 Mon Sep 17 00:00:00 2001 From: AaronColbyPrice <67345931+AaronColbyPrice@users.noreply.github.com> Date: Thu, 2 Jul 2020 07:58:50 -0700 Subject: [PATCH 131/318] Updating Conversant endpoint url (#1376) --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 16bab2996be..bb2b909f5de 100755 --- a/config/config.go +++ b/config/config.go @@ -791,7 +791,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um") v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs") v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2") - v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/s2s/header/24") + v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25") v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") From 47c7a6b71d9f9cc8e2b66d218e5d896d0b614430 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Thu, 2 Jul 2020 11:08:51 -0400 Subject: [PATCH 132/318] Metrics for TCF 2 adoption (#1360) --- exchange/exchange.go | 5 +++- exchange/utils.go | 20 +++++++++++++- exchange/utils_test.go | 6 ++--- go.sum | 3 +++ pbsmetrics/config/metrics.go | 11 ++++++++ pbsmetrics/go_metrics.go | 30 +++++++++++++++++++++ pbsmetrics/go_metrics_test.go | 4 +++ pbsmetrics/metrics.go | 30 +++++++++++++++++++++ pbsmetrics/metrics_mock.go | 5 ++++ pbsmetrics/prometheus/preload.go | 5 ++++ pbsmetrics/prometheus/prometheus.go | 27 ++++++++++++++++--- pbsmetrics/prometheus/prometheus_test.go | 34 ++++++++++++++++++++++-- pbsmetrics/prometheus/type_conversion.go | 9 +++++++ 13 files changed, 178 insertions(+), 11 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index d7eab0f4475..174a0b3e0fc 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -104,8 +104,11 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + cleanRequests, aliases, cleanMetrics, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + if cleanMetrics.gdprEnforced { + e.me.RecordTCFReq(pbsmetrics.TCFVersionToValue(cleanMetrics.gdprTcfVersion)) + } // List of bidders we have requests for. liveAdapters := listBiddersWithRequests(cleanRequests) diff --git a/exchange/utils.go b/exchange/utils.go index 54122d13c09..96c00ec0e36 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -6,6 +6,8 @@ import ( "fmt" "math/rand" + "github.com/prebid/go-gdpr/vendorconsent" + "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/config" @@ -17,6 +19,15 @@ import ( "github.com/prebid/prebid-server/privacy/lmt" ) +// cleanMetrics is a struct to export any metrics data resulting from cleanOpenRTBRequests(). It starts with just +// the TCF version, but made a struct to facilitate future expansion +type cleanMetrics struct { + // A simple flag if GDPR is being enforced on this request. + gdprEnforced bool + // a zero value means a missing or invalid GDPR string + gdprTcfVersion int +} + // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is: // // 1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder. @@ -29,7 +40,7 @@ func cleanOpenRTBRequests(ctx context.Context, labels pbsmetrics.Labels, gDPR gdpr.Permissions, usersyncIfAmbiguous bool, - privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, errs []error) { + privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, cleanMetrics cleanMetrics, errs []error) { impsByBidder, errs := splitImps(orig.Imp) if len(errs) > 0 { @@ -64,6 +75,13 @@ func cleanOpenRTBRequests(ctx context.Context, LMT: lmtPolicy.ShouldEnforce(), } + if gdpr == 1 { + cleanMetrics.gdprEnforced = true + parsedConsent, err := vendorconsent.ParseString(consent) + if err == nil { + cleanMetrics.gdprTcfVersion = int(parsedConsent.Version()) + } + } // bidder level privacy policies for bidder, bidReq := range requestsByBidder { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 4dad3f54648..e50d0f777f0 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -80,7 +80,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { } for _, test := range testCases { - reqByBidders, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -120,7 +120,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { }, } - results, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) @@ -182,7 +182,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { }, } - results, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) diff --git a/go.sum b/go.sum index 5d941b89e90..35b2b76591d 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtW github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A= +github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54= @@ -126,6 +128,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go index 4e249785ba6..3d105dead44 100644 --- a/pbsmetrics/config/metrics.go +++ b/pbsmetrics/config/metrics.go @@ -195,6 +195,13 @@ func (me *MultiMetricsEngine) RecordTimeoutNotice(success bool) { } } +// RecordTCFReq across all engines +func (me *MultiMetricsEngine) RecordTCFReq(version pbsmetrics.TCFVersionValue) { + for _, thisME := range *me { + thisME.RecordTCFReq(version) + } +} + // DummyMetricsEngine is a Noop metrics engine in case no metrics are configured. (may also be useful for tests) type DummyMetricsEngine struct{} @@ -273,3 +280,7 @@ func (me *DummyMetricsEngine) RecordRequestQueueTime(success bool, requestType p // RecordTimeoutNotice as a noop func (me *DummyMetricsEngine) RecordTimeoutNotice(success bool) { } + +// RecordReq as a noop +func (me *DummyMetricsEngine) RecordTCFReq(version pbsmetrics.TCFVersionValue) { +} diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go index 1ced4d57269..73eb30a1504 100644 --- a/pbsmetrics/go_metrics.go +++ b/pbsmetrics/go_metrics.go @@ -48,9 +48,13 @@ type Metrics struct { ImpsTypeAudio metrics.Meter ImpsTypeNative metrics.Meter + // Notification timeout metrics TimeoutNotificationSuccess metrics.Meter TimeoutNotificationFailure metrics.Meter + // TCF adaption metrics + TCFReqVersion map[TCFVersionValue]metrics.Meter + AdapterMetrics map[openrtb_ext.BidderName]*AdapterMetrics // Don't export accountMetrics because we need helper functions here to insure its properly populated dynamically accountMetrics map[string]*accountMetrics @@ -137,6 +141,8 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa TimeoutNotificationSuccess: blankMeter, TimeoutNotificationFailure: blankMeter, + TCFReqVersion: make(map[TCFVersionValue]metrics.Meter, len(TCFVersions())), + AdapterMetrics: make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)), accountMetrics: make(map[string]*accountMetrics), MetricsDisabled: disableMetrics, @@ -154,6 +160,15 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa } } + for _, c := range CacheResults() { + newMetrics.StoredReqCacheMeter[c] = blankMeter + newMetrics.StoredImpCacheMeter[c] = blankMeter + } + + for _, v := range TCFVersions() { + newMetrics.TCFReqVersion[v] = blankMeter + } + //to minimize memory usage, queuedTimeout metric is now supported for video endpoint only //boolean value represents 2 general request statuses: accepted and rejected newMetrics.RequestsQueueTimer["video"] = make(map[bool]metrics.Timer) @@ -218,6 +233,11 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d newMetrics.TimeoutNotificationSuccess = metrics.GetOrRegisterMeter("timeout_notification.ok", registry) newMetrics.TimeoutNotificationFailure = metrics.GetOrRegisterMeter("timeout_notification.failed", registry) + + for _, version := range TCFVersions() { + newMetrics.TCFReqVersion[version] = metrics.GetOrRegisterMeter(fmt.Sprintf("privacy.request.tcf.%s", string(version)), registry) + } + return newMetrics } @@ -562,6 +582,16 @@ func (me *Metrics) RecordTimeoutNotice(success bool) { return } +func (me *Metrics) RecordTCFReq(version TCFVersionValue) { + met, ok := me.TCFReqVersion[version] + if ok { + met.Mark(1) + } else { + me.TCFReqVersion[TCFVersionErr].Mark(1) + } + return +} + func doMark(bidder openrtb_ext.BidderName, meters map[openrtb_ext.BidderName]metrics.Meter) { met, ok := meters[bidder] if ok { diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go index 25f75e77758..6d9eaf9f0e9 100644 --- a/pbsmetrics/go_metrics_test.go +++ b/pbsmetrics/go_metrics_test.go @@ -56,6 +56,10 @@ func TestNewMetrics(t *testing.T) { ensureContains(t, registry, "timeout_notification.ok", m.TimeoutNotificationSuccess) ensureContains(t, registry, "timeout_notification.failed", m.TimeoutNotificationFailure) + ensureContains(t, registry, "privacy.request.tcf.v1", m.TCFReqVersion[TCFVersionV1]) + ensureContains(t, registry, "privacy.request.tcf.v2", m.TCFReqVersion[TCFVersionV2]) + ensureContains(t, registry, "privacy.request.tcf.err", m.TCFReqVersion[TCFVersionErr]) + } func TestRecordBidType(t *testing.T) { diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index e65ba313338..0e94fe71e90 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -248,6 +248,35 @@ func RequestActions() []RequestAction { } } +// TCFVersionValue : The possible values for TCF versions +type TCFVersionValue string + +const ( + TCFVersionErr TCFVersionValue = "err" + TCFVersionV1 TCFVersionValue = "v1" + TCFVersionV2 TCFVersionValue = "v2" +) + +// TCFVersions rtuens the possible values for the TCF version +func TCFVersions() []TCFVersionValue { + return []TCFVersionValue{ + TCFVersionErr, + TCFVersionV1, + TCFVersionV2, + } +} + +// TCFVersionToValue takes an integer TCF version and returns the corresponding TCFVersionValue +func TCFVersionToValue(version int) TCFVersionValue { + switch { + case version == 1: + return TCFVersionV1 + case version == 2: + return TCFVersionV2 + } + return TCFVersionErr +} + // MetricsEngine is a generic interface to record PBS metrics into the desired backend // The first three metrics function fire off once per incoming request, so total metrics // will equal the total number of incoming requests. The remaining 5 fire off per outgoing @@ -276,4 +305,5 @@ type MetricsEngine interface { RecordPrebidCacheRequestTime(success bool, length time.Duration) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) RecordTimeoutNotice(sucess bool) + RecordTCFReq(version TCFVersionValue) } diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go index 482cbf24fae..a6d36a72401 100644 --- a/pbsmetrics/metrics_mock.go +++ b/pbsmetrics/metrics_mock.go @@ -106,3 +106,8 @@ func (me *MetricsEngineMock) RecordRequestQueueTime(success bool, requestType Re func (me *MetricsEngineMock) RecordTimeoutNotice(success bool) { me.Called(success) } + +// RecordTCFReq mock +func (me *MetricsEngineMock) RecordTCFReq(version TCFVersionValue) { + me.Called(version) +} diff --git a/pbsmetrics/prometheus/preload.go b/pbsmetrics/prometheus/preload.go index 11e6bdc14d8..19f4f225af9 100644 --- a/pbsmetrics/prometheus/preload.go +++ b/pbsmetrics/prometheus/preload.go @@ -99,6 +99,11 @@ func preloadLabelValues(m *Metrics) { requestTypeLabel: {string(pbsmetrics.ReqTypeVideo)}, requestStatusLabel: {requestSuccessLabel, requestRejectLabel}, }) + + preloadLabelValuesForCounter(m.tcfVersion, map[string][]string{ + versionLabel: tcfVersionsAsString(), + sourceLabel: {string(sourceRequest)}, + }) } func preloadLabelValuesForCounter(counter *prometheus.CounterVec, labelsWithValues map[string][]string) { diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index e385b044981..bf854746fd2 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -28,7 +28,8 @@ type Metrics struct { requestsWithoutCookie *prometheus.CounterVec storedImpressionsCacheResult *prometheus.CounterVec storedRequestCacheResult *prometheus.CounterVec - timeout_notifications *prometheus.CounterVec + timeoutNotifications *prometheus.CounterVec + tcfVersion *prometheus.CounterVec // Adapter Metrics adapterBids *prometheus.CounterVec @@ -63,6 +64,7 @@ const ( requestStatusLabel = "request_status" requestTypeLabel = "request_type" successLabel = "success" + versionLabel = "version" ) const ( @@ -85,6 +87,11 @@ const ( requestFailed = "failed" ) +const ( + sourceLabel = "source" + sourceRequest = "request" +) + // NewMetrics initializes a new Prometheus metrics instance with preloaded label values. func NewMetrics(cfg config.PrometheusMetrics) *Metrics { requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} @@ -153,11 +160,16 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of stored request cache requests attempts by hits or miss.", []string{cacheResultLabel}) - metrics.timeout_notifications = newCounter(cfg, metrics.Registry, + metrics.timeoutNotifications = newCounter(cfg, metrics.Registry, "timeout_notification", "Count of timeout notifications triggered, and if they were successfully sent.", []string{successLabel}) + metrics.tcfVersion = newCounter(cfg, metrics.Registry, + "privacy_tcf", + "Count of TCF versions for requests where GDPR was enforced.", + []string{versionLabel, sourceLabel}) + metrics.adapterBids = newCounter(cfg, metrics.Registry, "adapter_bids", "Count of bids labeled by adapter and markup delivery type (adm or nurl).", @@ -412,12 +424,19 @@ func (m *Metrics) RecordRequestQueueTime(success bool, requestType pbsmetrics.Re func (m *Metrics) RecordTimeoutNotice(success bool) { if success { - m.timeout_notifications.With(prometheus.Labels{ + m.timeoutNotifications.With(prometheus.Labels{ successLabel: requestSuccessful, }).Inc() } else { - m.timeout_notifications.With(prometheus.Labels{ + m.timeoutNotifications.With(prometheus.Labels{ successLabel: requestFailed, }).Inc() } } + +func (m *Metrics) RecordTCFReq(version pbsmetrics.TCFVersionValue) { + m.tcfVersion.With(prometheus.Labels{ + versionLabel: string(version), + sourceLabel: sourceRequest, + }).Inc() +} diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index 24c50492139..03daff0d56b 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -930,13 +930,13 @@ func TestTimeoutNotifications(t *testing.T) { m.RecordTimeoutNotice(true) m.RecordTimeoutNotice(false) - assertCounterVecValue(t, "", "timeout_notifications:ok", m.timeout_notifications, + assertCounterVecValue(t, "", "timeout_notifications:ok", m.timeoutNotifications, float64(2), prometheus.Labels{ successLabel: requestSuccessful, }) - assertCounterVecValue(t, "", "timeout_notifications:fail", m.timeout_notifications, + assertCounterVecValue(t, "", "timeout_notifications:fail", m.timeoutNotifications, float64(1), prometheus.Labels{ successLabel: requestFailed, @@ -944,6 +944,36 @@ func TestTimeoutNotifications(t *testing.T) { } +func TestTCFMetrics(t *testing.T) { + m := createMetricsForTesting() + + m.RecordTCFReq(pbsmetrics.TCFVersionToValue(0)) + m.RecordTCFReq(pbsmetrics.TCFVersionToValue(1)) + m.RecordTCFReq(pbsmetrics.TCFVersionToValue(2)) + m.RecordTCFReq(pbsmetrics.TCFVersionToValue(1)) + + assertCounterVecValue(t, "", "privacy_tcf:err", m.tcfVersion, + float64(1), + prometheus.Labels{ + versionLabel: "err", + sourceLabel: sourceRequest, + }) + + assertCounterVecValue(t, "", "privacy_tcf:v1", m.tcfVersion, + float64(2), + prometheus.Labels{ + versionLabel: "v1", + sourceLabel: sourceRequest, + }) + + assertCounterVecValue(t, "", "privacy_tcf:v2", m.tcfVersion, + float64(1), + prometheus.Labels{ + versionLabel: "v2", + sourceLabel: sourceRequest, + }) +} + func assertCounterValue(t *testing.T, description, name string, counter prometheus.Counter, expected float64) { m := dto.Metric{} counter.Write(&m) diff --git a/pbsmetrics/prometheus/type_conversion.go b/pbsmetrics/prometheus/type_conversion.go index 8294ede0617..55a7092ed6d 100644 --- a/pbsmetrics/prometheus/type_conversion.go +++ b/pbsmetrics/prometheus/type_conversion.go @@ -76,3 +76,12 @@ func requestTypesAsString() []string { } return valuesAsString } + +func tcfVersionsAsString() []string { + values := pbsmetrics.TCFVersions() + valuesAsString := make([]string, len(values)) + for i, v := range values { + valuesAsString[i] = string(v) + } + return valuesAsString +} From 1d276d5d9ad24dddee29d2cd5dc709645e572eb5 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Thu, 2 Jul 2020 11:19:58 -0400 Subject: [PATCH 133/318] =?UTF-8?q?Fall=20back=20to=20constant=20rates=20w?= =?UTF-8?q?hen=20the=20currency=20rates=20endpoint=20i=E2=80=A6=20(#1364)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 2 + currencies/rate_converter.go | 77 ++++++--- currencies/rate_converter_test.go | 265 +++++++++++++++++++++--------- exchange/bidder_test.go | 2 + main.go | 4 +- 5 files changed, 248 insertions(+), 102 deletions(-) diff --git a/config/config.go b/config/config.go index bb2b909f5de..cc1d4a0ab4e 100755 --- a/config/config.go +++ b/config/config.go @@ -215,6 +215,7 @@ type Analytics struct { type CurrencyConverter struct { FetchURL string `mapstructure:"fetch_url"` FetchIntervalSeconds int `mapstructure:"fetch_interval_seconds"` + StaleRatesSeconds int `mapstructure:"stale_rates_seconds"` } func (cfg *CurrencyConverter) validate(errs configErrors) configErrors { @@ -866,6 +867,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("lmt.enforce", true) v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") v.SetDefault("currency_converter.fetch_interval_seconds", 1800) // fetch currency rates every 30 minutes + v.SetDefault("currency_converter.stale_rates_seconds", 0) v.SetDefault("default_request.type", "") v.SetDefault("default_request.file.name", "") v.SetDefault("default_request.alias_info", false) diff --git a/currencies/rate_converter.go b/currencies/rate_converter.go index 6c6ed172652..d22f347b17c 100644 --- a/currencies/rate_converter.go +++ b/currencies/rate_converter.go @@ -12,14 +12,15 @@ import ( // RateConverter holds the currencies conversion rates dictionary type RateConverter struct { - httpClient httpClient - done chan bool - updateNotifier chan<- int - fetchingInterval time.Duration - syncSourceURL string - rates atomic.Value // Should only hold Rates struct - lastUpdated atomic.Value // Should only hold time.Time - constantRates Conversions + httpClient httpClient + done chan bool + updateNotifier chan<- int + fetchingInterval time.Duration + staleRatesThreshold time.Duration + syncSourceURL string + rates atomic.Value // Should only hold Rates struct + lastUpdated atomic.Value // Should only hold time.Time + constantRates Conversions } // NewRateConverter returns a new RateConverter @@ -27,11 +28,13 @@ func NewRateConverter( httpClient httpClient, syncSourceURL string, fetchingInterval time.Duration, + staleRatesThreshold time.Duration, ) *RateConverter { return NewRateConverterWithNotifier( httpClient, syncSourceURL, fetchingInterval, + staleRatesThreshold, nil, // no notifier channel specified, won't send any notifications ) } @@ -40,7 +43,7 @@ func NewRateConverter( // By default there will be no currencies conversions done. // `currencies.ConstantRate` will be used. func NewRateConverterDefault() *RateConverter { - return NewRateConverter(&http.Client{}, "", time.Duration(0)) + return NewRateConverter(&http.Client{}, "", time.Duration(0), time.Duration(0)) } // NewRateConverterWithNotifier returns a new RateConverter @@ -51,22 +54,24 @@ func NewRateConverterWithNotifier( httpClient httpClient, syncSourceURL string, fetchingInterval time.Duration, + staleRatesThreshold time.Duration, updateNotifier chan<- int, ) *RateConverter { rc := &RateConverter{ - httpClient: httpClient, - done: make(chan bool), - updateNotifier: updateNotifier, - fetchingInterval: fetchingInterval, - syncSourceURL: syncSourceURL, - rates: atomic.Value{}, - lastUpdated: atomic.Value{}, + httpClient: httpClient, + done: make(chan bool), + updateNotifier: updateNotifier, + fetchingInterval: fetchingInterval, + staleRatesThreshold: staleRatesThreshold, + syncSourceURL: syncSourceURL, + rates: atomic.Value{}, + lastUpdated: atomic.Value{}, + constantRates: NewConstantRates(), } // In case host do not want to support currency lookup // we just stop here and do nothing if rc.fetchingInterval == time.Duration(0) { - rc.constantRates = NewConstantRates() return rc } @@ -111,7 +116,12 @@ func (rc *RateConverter) Update() error { rc.rates.Store(rates) rc.lastUpdated.Store(time.Now()) } else { - glog.Errorf("Error updating conversion rates: %v", err) + if rc.CheckStaleRates() { + rc.ClearRates() + glog.Errorf("Error updating conversion rates, falling back to constant rates: %v", err) + } else { + glog.Errorf("Error updating conversion rates: %v", err) + } } return err @@ -160,14 +170,33 @@ func (rc *RateConverter) LastUpdated() time.Time { // Rates returns current conversions rates func (rc *RateConverter) Rates() Conversions { - if rc.constantRates != nil { - // Converter is not active, returning the constant rates - return rc.constantRates - } - if rates := rc.rates.Load(); rates != nil { + // atomic.Value field rates is an empty interface and will be of type *Rates the first time rates are stored + // or nil if the rates have never been stored + if rates := rc.rates.Load(); rates != (*Rates)(nil) && rates != nil { return rates.(*Rates) } - return nil + return rc.constantRates +} + +// ClearRates sets the rates to nil +func (rc *RateConverter) ClearRates() { + // atomic.Value field rates must be of type *Rates so we cast nil to that type + rc.rates.Store((*Rates)(nil)) +} + +// CheckStaleRates checks if loaded third party conversion rates are stale +func (rc *RateConverter) CheckStaleRates() bool { + if rc.staleRatesThreshold <= 0 { + return false + } + currentTime := time.Now().UTC() + if lastUpdated := rc.lastUpdated.Load(); lastUpdated != nil { + delta := currentTime.Sub(lastUpdated.(time.Time).UTC()) + if delta.Seconds() > rc.staleRatesThreshold.Seconds() { + return true + } + } + return false } // GetInfo returns setup information about the converter diff --git a/currencies/rate_converter_test.go b/currencies/rate_converter_test.go index cb5e2a0be54..d717d1a3f9c 100644 --- a/currencies/rate_converter_test.go +++ b/currencies/rate_converter_test.go @@ -13,6 +13,20 @@ import ( "github.com/stretchr/testify/assert" ) +func getMockRates() []byte { + return []byte(`{ + "dataAsOf":"2018-09-12", + "conversions":{ + "USD":{ + "GBP":0.77208 + }, + "GBP":{ + "USD":1.2952 + } + } + }`) +} + func TestFetch_Success(t *testing.T) { // Setup: @@ -21,19 +35,7 @@ func TestFetch_Success(t *testing.T) { func(rw http.ResponseWriter, req *http.Request) { calledURLs = append(calledURLs, req.RequestURI) rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - }`, - )) + rw.Write([]byte(getMockRates())) }), ) @@ -57,6 +59,7 @@ func TestFetch_Success(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: @@ -87,12 +90,13 @@ func TestFetch_Fail404(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -114,12 +118,13 @@ func TestFetch_FailErrorHttpClient(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -132,11 +137,12 @@ func TestFetch_FailBadSyncURL(t *testing.T) { &http.Client{}, "justaweirdurl", time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -172,12 +178,13 @@ func TestFetch_FailBadJSON(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -200,12 +207,13 @@ func TestFetch_InvalidRemoteResponseContent(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(24)*time.Hour, + time.Duration(24)*time.Hour, ) // Verify: assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Nil(t, currencyConverter.Rates(), "Rates() should return nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -215,19 +223,7 @@ func TestInit(t *testing.T) { mockedHttpServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - }`, - )) + rw.Write([]byte(getMockRates())) }), ) @@ -239,6 +235,7 @@ func TestInit(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(100)*time.Millisecond, + time.Duration(24)*time.Hour, ticks, ) @@ -266,10 +263,8 @@ func TestInit(t *testing.T) { assert.False(t, intervalDiff > float64(errorMargin*100), "Interval between ticks should be: %d but was: %d", expectedIntervalDuration, intervalDuration) } - assert.NotNil(t, currencyConverter.Rates(), "Rates shouldn't be nil") assert.NotEqual(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated should be set") - rates := currencyConverter.Rates() - assert.Equal(t, expectedRates, rates, "Conversions.Rates weren't the expected ones") + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") if ticksCount == expectedTicks { @@ -287,19 +282,7 @@ func TestStop(t *testing.T) { func(rw http.ResponseWriter, req *http.Request) { calledURLs = append(calledURLs, req.RequestURI) rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - }`, - )) + rw.Write([]byte(getMockRates())) }), ) @@ -310,6 +293,7 @@ func TestStop(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(100)*time.Millisecond, + time.Duration(24)*time.Hour, ticks, ) @@ -337,19 +321,7 @@ func TestInitWithZeroDuration(t *testing.T) { func(rw http.ResponseWriter, req *http.Request) { calledURLs = append(calledURLs, req.RequestURI) rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - }`, - )) + rw.Write([]byte(getMockRates())) }), ) @@ -358,6 +330,7 @@ func TestInitWithZeroDuration(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(0), + time.Duration(24)*time.Hour, ) // Verify: @@ -366,8 +339,7 @@ func TestInitWithZeroDuration(t *testing.T) { assert.Equal(t, 0, len(calledURLs), "sync URL shouldn't have been called but was called %d times", 0, len(calledURLs)) assert.Equal(t, (time.Time{}), currencyConverter.LastUpdated(), "LastUpdated() shouldn't be set") - _, ok := currencyConverter.Rates().(*currencies.ConstantRates) - assert.True(t, ok, "Rates should be type of `currencies.ConstantRates`") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") } @@ -393,19 +365,7 @@ func TestRates(t *testing.T) { mockedHttpServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - }`, - )) + rw.Write([]byte(getMockRates())) }), ) @@ -415,6 +375,7 @@ func TestRates(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(100)*time.Millisecond, + time.Duration(24)*time.Hour, ticks, ) rates := currencyConverter.Rates() @@ -456,12 +417,161 @@ func TestRates_EmptyRates(t *testing.T) { &http.Client{}, mockedHttpServer.URL, time.Duration(100)*time.Millisecond, + time.Duration(24)*time.Hour, ) defer currencyConverter.StopPeriodicFetching() - rates := currencyConverter.Rates() // Verify: - assert.Nil(t, rates, "rates should be nil") + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") +} + +func TestSelectRatesBasedOnStaleness(t *testing.T) { + calledURLs := []string{} + callCnt := 0 + mockedHttpServer := httptest.NewServer(http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + calledURLs = append(calledURLs, req.RequestURI) + if callCnt == 0 || callCnt >= 5 { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(getMockRates())) + } else { + rw.WriteHeader(http.StatusNotFound) + } + callCnt++ + }), + ) + + defer mockedHttpServer.Close() + + expectedRates := ¤cies.Rates{ + DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), + Conversions: map[string]map[string]float64{ + "USD": { + "GBP": 0.77208, + }, + "GBP": { + "USD": 1.2952, + }, + }, + } + + // Execute: + currencyConverter := currencies.NewRateConverter( + &http.Client{}, + mockedHttpServer.URL, + time.Duration(100)*time.Millisecond, + time.Duration(200)*time.Millisecond, + ) + + // Verify: + // Rates are valid at t=0, then invalid for 500ms before being valid again + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + + time.Sleep(100 * time.Millisecond) + // Rates have been invalid for ~100ms, rates not stale yet + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + + time.Sleep(200 * time.Millisecond) + // Rates have been invalid for ~300ms, rates are stale + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + + time.Sleep(300 * time.Millisecond) + // Rates have been valid again for ~100ms + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") +} + +func TestUseConstantRatesUntilFetchIsSuccessful(t *testing.T) { + callCnt := 0 + mockedHttpServer := httptest.NewServer(http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + if callCnt >= 5 { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(getMockRates())) + } else { + rw.WriteHeader(http.StatusNotFound) + } + callCnt++ + }), + ) + + defer mockedHttpServer.Close() + + expectedRates := ¤cies.Rates{ + DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), + Conversions: map[string]map[string]float64{ + "USD": { + "GBP": 0.77208, + }, + "GBP": { + "USD": 1.2952, + }, + }, + } + + // Execute: + currencyConverter := currencies.NewRateConverter( + &http.Client{}, + mockedHttpServer.URL, + time.Duration(100)*time.Millisecond, + time.Duration(1)*time.Second, + ) + + // Verify: + // Rates are invalid at t=0 and remain invalid until 500ms have elapsed + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + + time.Sleep(400 * time.Millisecond) + // Rates have been invalid for ~400ms + assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + + time.Sleep(200 * time.Millisecond) + // Rates have been valid for ~100ms + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") +} + +func TestRatesAreNeverStale(t *testing.T) { + callCnt := 0 + mockedHttpServer := httptest.NewServer(http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + if callCnt == 0 { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(getMockRates())) + } else { + rw.WriteHeader(http.StatusNotFound) + } + callCnt++ + }), + ) + + defer mockedHttpServer.Close() + + expectedRates := ¤cies.Rates{ + DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), + Conversions: map[string]map[string]float64{ + "USD": { + "GBP": 0.77208, + }, + "GBP": { + "USD": 1.2952, + }, + }, + } + + // Execute: + currencyConverter := currencies.NewRateConverter( + &http.Client{}, + mockedHttpServer.URL, + time.Duration(100)*time.Millisecond, + time.Duration(0)*time.Millisecond, + ) + + // Verify: + // Rates are valid at t=0 and are then invalid at 100ms + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + + time.Sleep(500 * time.Millisecond) + // Rates have been invalid for ~400ms + assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") } func TestRace(t *testing.T) { @@ -495,6 +605,7 @@ func TestRace(t *testing.T) { mockedHttpClient, "currency.fake.com", time.Duration(10)*time.Millisecond, + time.Duration(24)*time.Hour, ) defer currencyConverter.StopPeriodicFetching() diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index fff397f0084..b776715adaf 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -517,6 +517,7 @@ func TestMultiCurrencies(t *testing.T) { &http.Client{}, mockedHTTPServer.URL, time.Duration(10)*time.Second, + time.Duration(24)*time.Hour, ) seatBid, errs := bidder.requestBid( context.Background(), @@ -831,6 +832,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { &http.Client{}, mockedHTTPServer.URL, time.Duration(10)*time.Second, + time.Duration(24)*time.Hour, ) seatBid, errs := bidder.requestBid( context.Background(), diff --git a/main.go b/main.go index d6ba430f059..9a835f42a4c 100644 --- a/main.go +++ b/main.go @@ -52,7 +52,9 @@ func loadConfig() (*config.Configuration, error) { func serve(revision string, cfg *config.Configuration) error { fetchingInterval := time.Duration(cfg.CurrencyConverter.FetchIntervalSeconds) * time.Second - currencyConverter := currencies.NewRateConverter(&http.Client{}, cfg.CurrencyConverter.FetchURL, fetchingInterval) + staleRatesThreshold := time.Duration(cfg.CurrencyConverter.StaleRatesSeconds) * time.Second + currencyConverter := currencies.NewRateConverter(&http.Client{}, cfg.CurrencyConverter.FetchURL, + fetchingInterval, staleRatesThreshold) r, err := router.New(cfg, currencyConverter) if err != nil { From 33f36b6be002e8993fe66be8985c6e95938512fb Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Mon, 6 Jul 2020 17:12:07 +0300 Subject: [PATCH 134/318] TheMediaGrid: added app type support (#1377) --- static/bidder-info/grid.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/static/bidder-info/grid.yaml b/static/bidder-info/grid.yaml index 9594830c0d0..325421a2c05 100644 --- a/static/bidder-info/grid.yaml +++ b/static/bidder-info/grid.yaml @@ -1,7 +1,11 @@ maintainer: email: "grid-tech@themediagrid.com" capabilities: - site: + app: mediaTypes: - banner - video + site: + mediaTypes: + - banner + - video \ No newline at end of file From 0f2dc5f7bec5a13a65792dbc33c4fa759f2b6899 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Wed, 8 Jul 2020 01:39:27 +0300 Subject: [PATCH 135/318] user.ext.eids support in adform adapter (#1381) --- adapters/adform/adform.go | 31 +++++++++++++++++++++++++++++++ adapters/adform/adform_test.go | 31 ++++++++++++++++++++++++++++++- openrtb_ext/user.go | 5 +++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go index 3aeea62ebde..69f1c12f073 100644 --- a/adapters/adform/adform.go +++ b/adapters/adform/adform.go @@ -42,6 +42,7 @@ type adformRequest struct { consent string digitrust *adformDigitrust currency string + eids string } type adformDigitrust struct { @@ -279,6 +280,9 @@ func (r *adformRequest) buildAdformUrl(a *AdformAdapter) string { parameters.Add("gdpr", r.gdprApplies) parameters.Add("gdpr_consent", r.consent) + if r.eids != "" { + parameters.Add("eids", r.eids) + } URL := *a.URL URL.RawQuery = parameters.Encode() @@ -465,6 +469,7 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro } } + eids := "" consent := "" var digitrustData *openrtb_ext.ExtUserDigiTrust if request.User != nil { @@ -472,6 +477,7 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro if err := json.Unmarshal(request.User.Ext, &extUser); err == nil { consent = extUser.Consent digitrustData = extUser.DigiTrust + eids = encodeEids(extUser.Eids) } } @@ -513,9 +519,34 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro consent: consent, digitrust: digitrust, currency: requestCurrency, + eids: eids, }, errors } +func encodeEids(eids []openrtb_ext.ExtUserEid) string { + if eids == nil { + return "" + } + + eidsMap := make(map[string]map[string][]int) + for _, eid := range eids { + _, ok := eidsMap[eid.Source] + if !ok { + eidsMap[eid.Source] = make(map[string][]int) + } + for _, uid := range eid.Uids { + eidsMap[eid.Source][uid.ID] = append(eidsMap[eid.Source][uid.ID], uid.Atype) + } + } + + encodedEids := "" + if eidsString, err := json.Marshal(eidsMap); err == nil { + encodedEids = base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(eidsString) + } + + return encodedEids +} + func getIPSafely(device *openrtb.Device) string { if device == nil { return "" diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index 63646f5f7f5..2fca7d1722d 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -480,7 +480,33 @@ func getUserExt() []byte { KeyV: 1, Pref: 0, } + + eids := []openrtb_ext.ExtUserEid{ + { + Source: "test.com", + Uids: []openrtb_ext.ExtUserEidUid{ + { + ID: "some_user_id", + Atype: 1, + }, + { + ID: "other_user_id", + }, + }, + }, + { + Source: "test2.org", + Uids: []openrtb_ext.ExtUserEidUid{ + { + ID: "other_user_id", + Atype: 2, + }, + }, + }, + } + userExt := openrtb_ext.ExtUser{ + Eids: eids, Consent: "abc", DigiTrust: &digitrust, } @@ -519,13 +545,16 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request, isOpenRtb boo } var midsWithCurrency = "" + var queryString = "" if isOpenRtb { midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9RVVSJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZQ&bWlkPTMyMzQ1JnJjdXI9RVVS&bWlkPTMyMzQ2JnJjdXI9RVVS" + queryString = "CC=1&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&eids=eyJ0ZXN0LmNvbSI6eyJvdGhlcl91c2VyX2lkIjpbMF0sInNvbWVfdXNlcl9pZCI6WzFdfSwidGVzdDIub3JnIjp7Im90aGVyX3VzZXJfaWQiOlsyXX19&fd=1&gdpr=1&gdpr_consent=abc&ip=111.111.111.111&pt=gross&rp=4&stid=transaction-id&" + midsWithCurrency } else { midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9VVNEJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZQ&bWlkPTMyMzQ1JnJjdXI9VVNE&bWlkPTMyMzQ2JnJjdXI9VVNE" // no way to pass currency in legacy adapter + queryString = "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&" + midsWithCurrency } - 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&"+midsWithCurrency, r.URL.RawQuery, "Query string"); !ok { + if ok, err := equal(queryString, 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 { diff --git a/openrtb_ext/user.go b/openrtb_ext/user.go index 520d73a6ed1..b83f82330db 100644 --- a/openrtb_ext/user.go +++ b/openrtb_ext/user.go @@ -43,6 +43,7 @@ type ExtUserEid struct { // ExtUserEidUid defines the contract for bidrequest.user.ext.eids[i].uids[j] type ExtUserEidUid struct { - ID string `json:"id"` - Ext json.RawMessage `json:"ext,omitempty"` + ID string `json:"id"` + Atype int `json:"atype,omitempty"` + Ext json.RawMessage `json:"ext,omitempty"` } From 034928ebb64b0aecab6c935188027fd45614f2a8 Mon Sep 17 00:00:00 2001 From: logicad Date: Fri, 10 Jul 2020 01:23:53 +0900 Subject: [PATCH 136/318] Add Logicad adapter (#1382) --- adapters/logicad/logicad.go | 155 ++++++++++++++++++ adapters/logicad/logicad_test.go | 10 ++ .../logicad/logicadtest/exemplary/banner.json | 92 +++++++++++ .../logicadtest/params/race/banner.json | 3 + .../logicadtest/supplemental/checkImp.json | 15 ++ .../logicad/logicadtest/supplemental/ext.json | 31 ++++ .../logicadtest/supplemental/missingtid.json | 33 ++++ .../supplemental/multiImpSameTid.json | 112 +++++++++++++ .../supplemental/responseCode.json | 72 ++++++++ .../supplemental/responseNoBid.json | 66 ++++++++ .../logicadtest/supplemental/responsebid.json | 73 +++++++++ .../logicadtest/supplemental/site.json | 98 +++++++++++ adapters/logicad/params_test.go | 45 +++++ adapters/logicad/usersync.go | 12 ++ adapters/logicad/usersync_test.go | 31 ++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_logicad.go | 5 + static/bidder-info/logicad.yaml | 10 ++ static/bidder-params/logicad.json | 13 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 23 files changed, 885 insertions(+) create mode 100644 adapters/logicad/logicad.go create mode 100644 adapters/logicad/logicad_test.go create mode 100644 adapters/logicad/logicadtest/exemplary/banner.json create mode 100644 adapters/logicad/logicadtest/params/race/banner.json create mode 100644 adapters/logicad/logicadtest/supplemental/checkImp.json create mode 100644 adapters/logicad/logicadtest/supplemental/ext.json create mode 100644 adapters/logicad/logicadtest/supplemental/missingtid.json create mode 100644 adapters/logicad/logicadtest/supplemental/multiImpSameTid.json create mode 100644 adapters/logicad/logicadtest/supplemental/responseCode.json create mode 100644 adapters/logicad/logicadtest/supplemental/responseNoBid.json create mode 100644 adapters/logicad/logicadtest/supplemental/responsebid.json create mode 100644 adapters/logicad/logicadtest/supplemental/site.json create mode 100644 adapters/logicad/params_test.go create mode 100644 adapters/logicad/usersync.go create mode 100644 adapters/logicad/usersync_test.go create mode 100644 openrtb_ext/imp_logicad.go create mode 100644 static/bidder-info/logicad.yaml create mode 100644 static/bidder-params/logicad.json diff --git a/adapters/logicad/logicad.go b/adapters/logicad/logicad.go new file mode 100644 index 00000000000..e757705a7bd --- /dev/null +++ b/adapters/logicad/logicad.go @@ -0,0 +1,155 @@ +package logicad + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type LogicadAdapter struct { + endpoint string +} + +func (adapter *LogicadAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{Message: "No impression in the bid request"}} + } + + pub2impressions, imps, errs := getImpressionsInfo(request.Imp) + if len(pub2impressions) == 0 || len(imps) == 0 { + return nil, errs + } + + result := make([]*adapters.RequestData, 0, len(pub2impressions)) + for k, imps := range pub2impressions { + bidRequest, err := adapter.buildAdapterRequest(request, &k, imps) + if err != nil { + errs = append(errs, err) + } else { + result = append(result, bidRequest) + } + } + return result, errs +} + +func getImpressionsInfo(imps []openrtb.Imp) (map[openrtb_ext.ExtImpLogicad][]openrtb.Imp, []openrtb.Imp, []error) { + errors := make([]error, 0, len(imps)) + resImps := make([]openrtb.Imp, 0, len(imps)) + res := make(map[openrtb_ext.ExtImpLogicad][]openrtb.Imp) + + for _, imp := range imps { + impExt, err := getImpressionExt(&imp) + if err != nil { + errors = append(errors, err) + continue + } + if err := validateImpression(&impExt); err != nil { + errors = append(errors, err) + continue + } + + if res[impExt] == nil { + res[impExt] = make([]openrtb.Imp, 0) + } + res[impExt] = append(res[impExt], imp) + resImps = append(resImps, imp) + } + return res, resImps, errors +} + +func validateImpression(impExt *openrtb_ext.ExtImpLogicad) error { + if impExt.Tid == "" { + return &errortypes.BadInput{Message: "No tid value provided"} + } + return nil +} + +func getImpressionExt(imp *openrtb.Imp) (openrtb_ext.ExtImpLogicad, error) { + var bidderExt adapters.ExtImpBidder + var logicadExt openrtb_ext.ExtImpLogicad + + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return logicadExt, &errortypes.BadInput{ + Message: err.Error(), + } + } + if err := json.Unmarshal(bidderExt.Bidder, &logicadExt); err != nil { + return logicadExt, &errortypes.BadInput{ + Message: err.Error(), + } + } + return logicadExt, nil +} + +func (adapter *LogicadAdapter) buildAdapterRequest(prebidBidRequest *openrtb.BidRequest, params *openrtb_ext.ExtImpLogicad, imps []openrtb.Imp) (*adapters.RequestData, error) { + newBidRequest := createBidRequest(prebidBidRequest, params, imps) + reqJSON, err := json.Marshal(newBidRequest) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return &adapters.RequestData{ + Method: "POST", + Uri: adapter.endpoint, + Body: reqJSON, + Headers: headers}, nil +} + +func createBidRequest(prebidBidRequest *openrtb.BidRequest, params *openrtb_ext.ExtImpLogicad, imps []openrtb.Imp) *openrtb.BidRequest { + bidRequest := *prebidBidRequest + bidRequest.Imp = imps + for idx := range bidRequest.Imp { + imp := &bidRequest.Imp[idx] + imp.TagID = params.Tid + imp.Ext = nil + } + return &bidRequest +} + +//MakeBids translates Logicad bid response to prebid-server specific format +func (adapter *LogicadAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + if response.StatusCode != http.StatusOK { + msg := fmt.Sprintf("Unexpected http status code: %d", response.StatusCode) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + + } + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + msg := fmt.Sprintf("Bad server response: %d", err) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + } + if len(bidResp.SeatBid) != 1 { + msg := fmt.Sprintf("Invalid SeatBids count: %d", len(bidResp.SeatBid)) + return nil, []error{&errortypes.BadServerResponse{Message: msg}} + } + + seatBid := bidResp.SeatBid[0] + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(seatBid.Bid)) + + for i := 0; i < len(seatBid.Bid); i++ { + bid := seatBid.Bid[i] + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: openrtb_ext.BidTypeBanner, + }) + } + return bidResponse, nil +} + +func NewLogicadBidder(endpoint string) adapters.Bidder { + return &LogicadAdapter{ + endpoint: endpoint, + } +} diff --git a/adapters/logicad/logicad_test.go b/adapters/logicad/logicad_test.go new file mode 100644 index 00000000000..adf20e4ed33 --- /dev/null +++ b/adapters/logicad/logicad_test.go @@ -0,0 +1,10 @@ +package logicad + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "logicadtest", NewLogicadBidder("https://localhost/adrequest/prebidserver")) +} diff --git a/adapters/logicad/logicadtest/exemplary/banner.json b/adapters/logicad/logicadtest/exemplary/banner.json new file mode 100644 index 00000000000..f782cc2b9f8 --- /dev/null +++ b/adapters/logicad/logicadtest/exemplary/banner.json @@ -0,0 +1,92 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "tagid": "testtid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/logicad/logicadtest/params/race/banner.json b/adapters/logicad/logicadtest/params/race/banner.json new file mode 100644 index 00000000000..7cb3de5a1ef --- /dev/null +++ b/adapters/logicad/logicadtest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "tid": "testtid" +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/checkImp.json b/adapters/logicad/logicadtest/supplemental/checkImp.json new file mode 100644 index 00000000000..62c6e3e8f9e --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/checkImp.json @@ -0,0 +1,15 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test", + "domain": "test.com" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the bid request", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/ext.json b/adapters/logicad/logicadtest/supplemental/ext.json new file mode 100644 index 00000000000..ad35892086b --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/ext.json @@ -0,0 +1,31 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "tid": "testtid" + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/missingtid.json b/adapters/logicad/logicadtest/supplemental/missingtid.json new file mode 100644 index 00000000000..5ed84cef65e --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/missingtid.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No tid value provided", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/multiImpSameTid.json b/adapters/logicad/logicadtest/supplemental/multiImpSameTid.json new file mode 100644 index 00000000000..848733cdf35 --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/multiImpSameTid.json @@ -0,0 +1,112 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + }, + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "tagid": "testtid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + }, + { + "id": "testimpid", + "tagid": "testtid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/logicad/logicadtest/supplemental/responseCode.json b/adapters/logicad/logicadtest/supplemental/responseCode.json new file mode 100644 index 00000000000..471993ad8f2 --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/responseCode.json @@ -0,0 +1,72 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "id": "testimpid", + "tagid": "testtid" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "body": { + "seatbid": [] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected http status code: 0", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/responseNoBid.json b/adapters/logicad/logicadtest/supplemental/responseNoBid.json new file mode 100644 index 00000000000..6ddab2ab6bd --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/responseNoBid.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ], + "site": { + "id": "test" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "tagid": "testtid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + } + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/logicad/logicadtest/supplemental/responsebid.json b/adapters/logicad/logicadtest/supplemental/responsebid.json new file mode 100644 index 00000000000..59d1c2ac21e --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/responsebid.json @@ -0,0 +1,73 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "id": "testimpid", + "tagid": "testtid" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Invalid SeatBids count: 0", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/logicadtest/supplemental/site.json b/adapters/logicad/logicadtest/supplemental/site.json new file mode 100644 index 00000000000..c747413f91c --- /dev/null +++ b/adapters/logicad/logicadtest/supplemental/site.json @@ -0,0 +1,98 @@ +{ + "mockBidRequest": { + "id": "testid", + "site": { + "id": "test" + }, + "imp": [ + { + "id": "testimpid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "tid": "testtid" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://localhost/adrequest/prebidserver", + "body": { + "id": "testid", + "imp": [ + { + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 320, + "h": 50 + } + ] + }, + "id": "testimpid", + "tagid": "testtid" + } + ], + "site": { + "id": "test" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "123", + "adid": "456", + "price": 0.12, + "id": "testid", + "impid": "testimpid", + "cid": "789" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/logicad/params_test.go b/adapters/logicad/params_test.go new file mode 100644 index 00000000000..eb34452811b --- /dev/null +++ b/adapters/logicad/params_test.go @@ -0,0 +1,45 @@ +package logicad + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderLogicad, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected LunaMedia params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderLogicad, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"tid": "testtid"}`, +} + +var invalidParams = []string{ + `nil`, + ``, + `[]`, + `true`, + `{"tid": 42}`, +} diff --git a/adapters/logicad/usersync.go b/adapters/logicad/usersync.go new file mode 100644 index 00000000000..d26a197b0a1 --- /dev/null +++ b/adapters/logicad/usersync.go @@ -0,0 +1,12 @@ +package logicad + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewLogicadSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("logicad", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/logicad/usersync_test.go b/adapters/logicad/usersync_test.go new file mode 100644 index 00000000000..89d6207d348 --- /dev/null +++ b/adapters/logicad/usersync_test.go @@ -0,0 +1,31 @@ +package logicad + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestLogicadSyncer(t *testing.T) { + syncURL := "https://localhost/cookiesender?r=true&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ru=localhost%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewLogicadSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "A", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://localhost/cookiesender?r=true&gdpr=1&gdpr_consent=A&ru=localhost%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D1%26gdpr_consent%3DA%26uid%3D%24UID", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index cc1d4a0ab4e..65ad352c938 100755 --- a/config/config.go +++ b/config/config.go @@ -597,6 +597,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLogicad, "https://cr-p31.ladsp.jp/cookiesender/31?r=true&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlogicad%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLunaMedia, "https://api.lunamedia.io/xp/user-sync?redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlunamedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D") @@ -808,6 +809,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid") v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest") v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") + v.SetDefault("adapters.logicad.endpoint", "https://pbs.ladsp.com/adrequest/prebidserver") v.SetDefault("adapters.lunamedia.endpoint", "http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet") v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 1f62d232233..53607ac57d8 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -48,6 +48,7 @@ import ( "github.com/prebid/prebid-server/adapters/kubient" "github.com/prebid/prebid-server/adapters/lifestreet" "github.com/prebid/prebid-server/adapters/lockerdome" + "github.com/prebid/prebid-server/adapters/logicad" "github.com/prebid/prebid-server/adapters/lunamedia" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" @@ -133,6 +134,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), openrtb_ext.BidderLunaMedia: lunamedia.NewLunaMediaBidder(cfg.Adapters[string(openrtb_ext.BidderLunaMedia)].Endpoint), + openrtb_ext.BidderLogicad: logicad.NewLogicadBidder(cfg.Adapters[string(openrtb_ext.BidderLogicad)].Endpoint), openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), openrtb_ext.BidderMobileFuse: mobilefuse.NewMobileFuseBidder(cfg.Adapters[string(openrtb_ext.BidderMobileFuse)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 49d7b09d671..62fb9750616 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -64,6 +64,7 @@ const ( BidderKubient BidderName = "kubient" BidderLifestreet BidderName = "lifestreet" BidderLockerDome BidderName = "lockerdome" + BidderLogicad BidderName = "logicad" BidderLunaMedia BidderName = "lunamedia" BidderMarsmedia BidderName = "marsmedia" BidderMgid BidderName = "mgid" @@ -145,6 +146,7 @@ var BidderMap = map[string]BidderName{ "kubient": BidderKubient, "lifestreet": BidderLifestreet, "lockerdome": BidderLockerDome, + "logicad": BidderLogicad, "lunamedia": BidderLunaMedia, "marsmedia": BidderMarsmedia, "mgid": BidderMgid, diff --git a/openrtb_ext/imp_logicad.go b/openrtb_ext/imp_logicad.go new file mode 100644 index 00000000000..e4e3c3b091c --- /dev/null +++ b/openrtb_ext/imp_logicad.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpLogicad struct { + Tid string `json:"tid"` +} diff --git a/static/bidder-info/logicad.yaml b/static/bidder-info/logicad.yaml new file mode 100644 index 00000000000..c087516c061 --- /dev/null +++ b/static/bidder-info/logicad.yaml @@ -0,0 +1,10 @@ +maintainer: + email: "prebid@so-netmedia.jp" +capabilities: + site: + mediaTypes: + - banner + app: + mediaTypes: + - banner + diff --git a/static/bidder-params/logicad.json b/static/bidder-params/logicad.json new file mode 100644 index 00000000000..2a892f91266 --- /dev/null +++ b/static/bidder-params/logicad.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Logicad Adapter Params", + "description": "A schema which validates params accepted by the Logicad adapter", + "type": "object", + "properties": { + "tid": { + "type": "string", + "description": "Logicad for Publishers placement ID" + } + }, + "required": ["tid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index f1f643afb74..89540ea205b 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -39,6 +39,7 @@ import ( "github.com/prebid/prebid-server/adapters/ix" "github.com/prebid/prebid-server/adapters/lifestreet" "github.com/prebid/prebid-server/adapters/lockerdome" + "github.com/prebid/prebid-server/adapters/logicad" "github.com/prebid/prebid-server/adapters/lunamedia" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" @@ -115,6 +116,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderLogicad, logicad.NewLogicadSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLunaMedia, lunamedia.NewLunaMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMarsmedia, marsmedia.NewMarsmediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index b23541eaf8a..32ab2e730eb 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -48,6 +48,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderIx): syncConfig, string(openrtb_ext.BidderLifestreet): syncConfig, string(openrtb_ext.BidderLockerDome): syncConfig, + string(openrtb_ext.BidderLogicad): syncConfig, string(openrtb_ext.BidderLunaMedia): syncConfig, string(openrtb_ext.BidderMarsmedia): syncConfig, string(openrtb_ext.BidderMgid): syncConfig, From 7c3521b2c8e1bec25341cb54072e8770becea820 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Mon, 13 Jul 2020 23:35:29 -0400 Subject: [PATCH 137/318] Fix Previous Merge Conflict (#1392) --- config/config.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 65ad352c938..f33dba69b60 100755 --- a/config/config.go +++ b/config/config.go @@ -764,12 +764,8 @@ func SetupViper(v *viper.Viper, filename string) { // Disabling adapters by default that require some specific config params. // If you're using one of these, make sure you check out the documentation (https://github.com/prebid/prebid-server/tree/master/docs/bidders) // for them and specify all the parameters they need for them to work correctly. - v.SetDefault("adapters.audiencenetwork.disabled", true) - v.SetDefault("adapters.rubicon.disabled", true) v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb") v.SetDefault("adapters.33across.partner_id", "") - v.SetDefault("adapters.dmx.endpoint", "https://dmx.districtm.io/b/v2") - v.SetDefault("adapters.adtelligent.endpoint", "http://hb.adtelligent.com/auction") v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx") v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1") v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json") @@ -787,6 +783,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid") v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs v.SetDefault("adapters.appnexus.platform_id", "5") + v.SetDefault("adapters.audiencenetwork.disabled", true) v.SetDefault("adapters.avocet.disabled", true) v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display") v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") @@ -796,6 +793,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25") v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") + v.SetDefault("adapters.dmx.endpoint", "https://dmx.districtm.io/b/v2") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") v.SetDefault("adapters.eplanning.endpoint", "http://rtb.e-planning.net/pbs/1") @@ -823,6 +821,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.pulsepoint.endpoint", "http://bid.contextweb.com/header/s/ortb/prebid-s2s") v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp") v.SetDefault("adapters.rtbhouse.endpoint", "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids") + v.SetDefault("adapters.rubicon.disabled", true) v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json") v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") v.SetDefault("adapters.smartadserver.endpoint", "https://ssb.smartadserver.com") From bb2b03748ee9a7a83c09243762bfaf50b91dea77 Mon Sep 17 00:00:00 2001 From: Marsel Date: Wed, 15 Jul 2020 08:49:58 +0300 Subject: [PATCH 138/318] Kubient: Change default endpont address (#1398) --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index f33dba69b60..5d538f38523 100755 --- a/config/config.go +++ b/config/config.go @@ -804,7 +804,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs") v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932") v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server") - v.SetDefault("adapters.kubient.endpoint", "http://kbntx.ch/prebid") + v.SetDefault("adapters.kubient.endpoint", "https://kssp.kbntx.ch/prebid") v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest") v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") v.SetDefault("adapters.logicad.endpoint", "https://pbs.ladsp.com/adrequest/prebidserver") From e6d159e71dd479338207f4eb7f7746b71a0e3d9d Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Wed, 15 Jul 2020 10:07:53 -0400 Subject: [PATCH 139/318] Add support for multiple root schain nodes (#1374) --- endpoints/openrtb2/auction.go | 9 ++ endpoints/openrtb2/auction_test.go | 47 ++++++++ exchange/utils.go | 94 ++++++++++++++- exchange/utils_test.go | 180 +++++++++++++++++++++++++++++ openrtb_ext/request.go | 43 ++++++- 5 files changed, 366 insertions(+), 7 deletions(-) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 20acc2aedd3..3fd2132143e 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -290,6 +290,10 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { if err := validateBidAdjustmentFactors(bidExt.Prebid.BidAdjustmentFactors, aliases); err != nil { return []error{err} } + + if err := validateSChains(bidExt); err != nil { + return []error{err} + } } if (req.Site == nil && req.App == nil) || (req.Site != nil && req.App != nil) { @@ -362,6 +366,11 @@ func validateBidAdjustmentFactors(adjustmentFactors map[string]float64, aliases return nil } +func validateSChains(req *openrtb_ext.ExtRequest) error { + _, err := exchange.BidderToPrebidSChains(req) + return err +} + func (deps *endpointDeps) validateImp(imp *openrtb.Imp, aliases map[string]string, index int) []error { if imp.ID == "" { return []error{fmt.Errorf("request.imp[%d] missing required field: \"id\"", index)} diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 97f0038a392..c697c206483 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1039,6 +1039,53 @@ func TestCCPAInvalid(t *testing.T) { assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request") } +func TestSChainInvalid(t *testing.T) { + deps := &endpointDeps{ + &nobidExchange{}, + newParamsValidator(t), + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{}, + pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BidderMap, + nil, + nil, + hardcodedResponseIPValidator{response: true}, + } + + ui := uint64(1) + req := openrtb.BidRequest{ + ID: "someID", + Imp: []openrtb.Imp{ + { + ID: "imp-ID", + Banner: &openrtb.Banner{ + W: &ui, + H: &ui, + }, + Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`), + }, + }, + Site: &openrtb.Site{ + ID: "myID", + }, + Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"abcd"}`), + }, + Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), + } + + errL := deps.validateRequest(&req) + + expectedError := fmt.Errorf("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.") + assert.ElementsMatch(t, errL, []error{expectedError}) +} + func TestSanitizeRequest(t *testing.T) { testCases := []struct { description string diff --git a/exchange/utils.go b/exchange/utils.go index 96c00ec0e36..4de985eca40 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -28,6 +28,29 @@ type cleanMetrics struct { gdprTcfVersion int } +func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext.ExtRequestPrebidSChainSChain, error) { + bidderToSChains := make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) + + if len(req.Prebid.SChains) == 0 { + return bidderToSChains, nil + } + + for _, schainWrapper := range req.Prebid.SChains { + if schainWrapper != nil && len(schainWrapper.Bidders) > 0 { + for _, bidder := range schainWrapper.Bidders { + if _, present := bidderToSChains[bidder]; present { + return nil, fmt.Errorf("request.ext.prebid.schains contains multiple schains for bidder %s; "+ + "it must contain no more than one per bidder.", bidder) + } else { + bidderToSChains[bidder] = &schainWrapper.SChain + } + } + } + } + + return bidderToSChains, nil +} + // cleanOpenRTBRequests splits the input request into requests which are sanitized for each bidder. Intended behavior is: // // 1. BidRequest.Imp[].Ext will only contain the "prebid" field and a "bidder" field which has the params for the intended Bidder. @@ -103,12 +126,35 @@ func cleanOpenRTBRequests(ctx context.Context, return } -func splitBidRequest(req *openrtb.BidRequest, impsByBidder map[string][]openrtb.Imp, aliases map[string]string, usersyncs IdFetcher, blabels map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels, labels pbsmetrics.Labels) (map[openrtb_ext.BidderName]*openrtb.BidRequest, []error) { +func splitBidRequest(req *openrtb.BidRequest, + impsByBidder map[string][]openrtb.Imp, + aliases map[string]string, + usersyncs IdFetcher, + blabels map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels, + labels pbsmetrics.Labels) (map[openrtb_ext.BidderName]*openrtb.BidRequest, []error) { + requestsByBidder := make(map[openrtb_ext.BidderName]*openrtb.BidRequest, len(impsByBidder)) explicitBuyerUIDs, err := extractBuyerUIDs(req.User) if err != nil { return nil, []error{err} } + + var requestExt openrtb_ext.ExtRequest + var sChainsByBidder map[string]*openrtb_ext.ExtRequestPrebidSChainSChain + if len(req.Ext) > 0 { + err := json.Unmarshal(req.Ext, &requestExt) + if err != nil { + return nil, []error{err} + } + + sChainsByBidder, err = BidderToPrebidSChains(&requestExt) + if err != nil { + return nil, []error{err} + } + } else { + sChainsByBidder = make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) + } + for bidder, imps := range impsByBidder { reqCopy := *req coreBidder := resolveBidder(bidder, aliases) @@ -128,11 +174,57 @@ func splitBidRequest(req *openrtb.BidRequest, impsByBidder map[string][]openrtb. blabels[coreBidder].CookieFlag = pbsmetrics.CookieFlagYes } reqCopy.Imp = imps + + prepareSource(&reqCopy, bidder, sChainsByBidder) + prepareExt(&reqCopy, &requestExt) + requestsByBidder[openrtb_ext.BidderName(bidder)] = &reqCopy } return requestsByBidder, nil } +func prepareExt(req *openrtb.BidRequest, unpackedExt *openrtb_ext.ExtRequest) { + if len(req.Ext) == 0 { + return + } + extCopy := *unpackedExt + extCopy.Prebid.SChains = nil + reqExt, err := json.Marshal(extCopy) + if err == nil { + req.Ext = reqExt + } +} + +func prepareSource(req *openrtb.BidRequest, bidder string, sChainsByBidder map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) { + const sChainWildCard = "*" + var selectedSChain *openrtb_ext.ExtRequestPrebidSChainSChain + + wildCardSChain := sChainsByBidder[sChainWildCard] + bidderSChain := sChainsByBidder[bidder] + + // source should not be modified + if bidderSChain == nil && wildCardSChain == nil { + return + } + + if bidderSChain != nil { + selectedSChain = bidderSChain + } else { + selectedSChain = wildCardSChain + } + + // set source + var source openrtb.Source + schain := openrtb_ext.ExtRequestPrebidSChain{ + SChain: *selectedSChain, + } + sourceExt, err := json.Marshal(schain) + if err == nil { + source.Ext = sourceExt + req.Source = &source + } +} + // extractBuyerUIDs parses the values from user.ext.prebid.buyeruids, and then deletes those values from the ext. // This prevents a Bidder from using these values to figure out who else is involved in the Auction. func extractBuyerUIDs(user *openrtb.User) (map[string]string, error) { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index e50d0f777f0..6d66e816e7b 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -135,6 +135,92 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { } } +func TestCleanOpenRTBRequestsSChain(t *testing.T) { + testCases := []struct { + description string + inSourceExt json.RawMessage + inExt json.RawMessage + outSourceExt json.RawMessage + outExt json.RawMessage + hasError bool + }{ + { + description: "Empty root ext and source ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(``), + outSourceExt: json.RawMessage(``), + outExt: json.RawMessage(``), + hasError: false, + }, + { + description: "No schains in root ext and empty source ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[]}}`), + outSourceExt: json.RawMessage(``), + outExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, + }, + { + description: "Use source schain -- no bidder schain or wildcard schain in ext.prebid.schains", + inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["bidder1"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + outExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, + }, + { + description: "Use schain for bidder in ext.prebid.schains", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, + }, + { + description: "Use wildcard schain in ext.prebid.schains", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, + }, + { + description: "Use schain for bidder in ext.prebid.schains instead of wildcard", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"},"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"wildcard.com","sid":"wildcard1","rid":"WildcardReq1","hp":1}],"ver":"1.0"}} ]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"}}}`), + hasError: false, + }, + { + description: "Use source schain -- multiple (two) bidder schains in ext.prebid.schains", + inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: nil, + outExt: nil, + hasError: true, + }, + } + + for _, test := range testCases { + req := newBidRequest(t) + req.Source.Ext = test.inSourceExt + req.Ext = test.inExt + + results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, config.Privacy{}) + result := results["appnexus"] + + if test.hasError == true { + assert.NotNil(t, errs) + assert.Nil(t, result) + } else { + assert.Nil(t, errs) + assert.Equal(t, test.outSourceExt, result.Source.Ext, test.description+":Source.Ext") + assert.Equal(t, test.outExt, result.Ext, test.description+":Ext") + } + } +} + func TestCleanOpenRTBRequestsLMT(t *testing.T) { var ( enabled int8 = 1 @@ -302,5 +388,99 @@ func TestRandomizeList(t *testing.T) { if len(adapters) != 1 { t.Errorf("RandomizeList, expected a list of 1, found %d", len(adapters)) } +} + +func TestBidderToPrebidChains(t *testing.T) { + input := openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + SChains: []*openrtb_ext.ExtRequestPrebidSChain{ + { + Bidders: []string{"Bidder1", "Bidder2"}, + SChain: openrtb_ext.ExtRequestPrebidSChainSChain{ + Complete: 1, + Nodes: []*openrtb_ext.ExtRequestPrebidSChainSChainNode{ + { + ASI: "asi1", + SID: "sid1", + Name: "name1", + RID: "rid1", + Domain: "domain1", + HP: 1, + }, + { + ASI: "asi2", + SID: "sid2", + Name: "name2", + RID: "rid2", + Domain: "domain2", + HP: 2, + }, + }, + Ver: "version1", + }, + }, + { + Bidders: []string{"Bidder3", "Bidder4"}, + SChain: openrtb_ext.ExtRequestPrebidSChainSChain{}, + }, + }, + }, + } + + output, err := BidderToPrebidSChains(&input) + + assert.Nil(t, err) + assert.Equal(t, len(output), 4) + assert.Same(t, output["Bidder1"], &input.Prebid.SChains[0].SChain) + assert.Same(t, output["Bidder2"], &input.Prebid.SChains[0].SChain) + assert.Same(t, output["Bidder3"], &input.Prebid.SChains[1].SChain) + assert.Same(t, output["Bidder4"], &input.Prebid.SChains[1].SChain) +} + +func TestBidderToPrebidChainsDiscardMultipleChainsForBidder(t *testing.T) { + input := openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + SChains: []*openrtb_ext.ExtRequestPrebidSChain{ + { + Bidders: []string{"Bidder1"}, + SChain: openrtb_ext.ExtRequestPrebidSChainSChain{}, + }, + { + Bidders: []string{"Bidder1", "Bidder2"}, + SChain: openrtb_ext.ExtRequestPrebidSChainSChain{}, + }, + }, + }, + } + + output, err := BidderToPrebidSChains(&input) + + assert.NotNil(t, err) + assert.Nil(t, output) +} + +func TestBidderToPrebidChainsNilSChains(t *testing.T) { + input := openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + SChains: nil, + }, + } + + output, err := BidderToPrebidSChains(&input) + + assert.Nil(t, err) + assert.Equal(t, len(output), 0) +} + +func TestBidderToPrebidChainsZeroLengthSChains(t *testing.T) { + input := openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + SChains: []*openrtb_ext.ExtRequestPrebidSChain{}, + }, + } + + output, err := BidderToPrebidSChains(&input) + assert.Nil(t, err) + assert.Equal(t, len(output), 0) } diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 25b5c881408..86388f60cf4 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -12,12 +12,43 @@ type ExtRequest struct { // ExtRequestPrebid defines the contract for bidrequest.ext.prebid type ExtRequestPrebid struct { - Aliases map[string]string `json:"aliases,omitempty"` - BidAdjustmentFactors map[string]float64 `json:"bidadjustmentfactors,omitempty"` - Cache *ExtRequestPrebidCache `json:"cache,omitempty"` - StoredRequest *ExtStoredRequest `json:"storedrequest,omitempty"` - Targeting *ExtRequestTargeting `json:"targeting,omitempty"` - SupportDeals bool `json:"supportdeals,omitempty"` + Aliases map[string]string `json:"aliases,omitempty"` + BidAdjustmentFactors map[string]float64 `json:"bidadjustmentfactors,omitempty"` + Cache *ExtRequestPrebidCache `json:"cache,omitempty"` + SChains []*ExtRequestPrebidSChain `json:"schains,omitempty"` + StoredRequest *ExtStoredRequest `json:"storedrequest,omitempty"` + Targeting *ExtRequestTargeting `json:"targeting,omitempty"` + SupportDeals bool `json:"supportdeals,omitempty"` +} + +// ExtRequestPrebid defines the contract for bidrequest.ext.prebid.schains +type ExtRequestPrebidSChain struct { + Bidders []string `json:"bidders,omitempty"` + SChain ExtRequestPrebidSChainSChain `json:"schain"` +} + +// ExtRequestPrebidSChainSChain defines the contract for bidrequest.ext.prebid.schains[i].schain +type ExtRequestPrebidSChainSChain struct { + Complete int `json:"complete"` + Nodes []*ExtRequestPrebidSChainSChainNode `json:"nodes"` + Ver string `json:"ver"` + Ext json.RawMessage `json:"ext,omitempty"` +} + +// ExtRequestPrebidSChainSChainNode defines the contract for bidrequest.ext.prebid.schains[i].schain[i].nodes +type ExtRequestPrebidSChainSChainNode struct { + ASI string `json:"asi"` + SID string `json:"sid"` + RID string `json:"rid,omitempty"` + Name string `json:"name,omitempty"` + Domain string `json:"domain,omitempty"` + HP int `json:"hp"` + Ext json.RawMessage `json:"ext,omitempty"` +} + +// SourceExt defines the contract for bidrequest.source.ext +type SourceExt struct { + SChain ExtRequestPrebidSChainSChain `json:"schain"` } // ExtRequestPrebidCache defines the contract for bidrequest.ext.prebid.cache From e6fe57e058a0dd44273916d5c919829937ec13dc Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Wed, 15 Jul 2020 22:05:49 -0400 Subject: [PATCH 140/318] Update endpoint for latest release by districtm (#1401) Co-authored-by: steve-a-districtm --- adapters/dmx/dmx.go | 2 +- config/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go index 6b4f698d4b1..de33bd390e5 100644 --- a/adapters/dmx/dmx.go +++ b/adapters/dmx/dmx.go @@ -160,7 +160,7 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapte } headers := http.Header{} - headers.Add("Content-Type", "Application/json;charset=utf-8") + headers.Add("Content-Type", "application/json;charset=utf-8") reqBidder := &adapters.RequestData{ Method: "POST", Uri: adapter.endpoint + addParams(sellerId), //adapter.endpoint, diff --git a/config/config.go b/config/config.go index 5d538f38523..2e7f875b023 100755 --- a/config/config.go +++ b/config/config.go @@ -793,7 +793,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25") v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") - v.SetDefault("adapters.dmx.endpoint", "https://dmx.districtm.io/b/v2") + v.SetDefault("adapters.dmx.endpoint", "https://dmx-direct.districtm.io/b/v2") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") v.SetDefault("adapters.eplanning.endpoint", "http://rtb.e-planning.net/pbs/1") From ea348e3fd3fa4fcc04149e761676051539d92aa1 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 15 Jul 2020 23:01:29 -0400 Subject: [PATCH 141/318] Set OpenRTB DNT From HTTP Header (#1397) --- endpoints/openrtb2/auction.go | 26 +++ endpoints/openrtb2/auction_test.go | 183 ++++++++++++++++++ .../supplementary/site-has-dnt.json | 45 +++++ 3 files changed, 254 insertions(+) create mode 100644 endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-dnt.json diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 3fd2132143e..86186fa8373 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -39,6 +39,12 @@ import ( const storedRequestTimeoutMillis = 50 +var ( + dntKey string = http.CanonicalHeaderKey("DNT") + dntDisabled int8 = 0 + dntEnabled int8 = 1 +) + func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName) (httprouter.Handle, error) { if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { @@ -964,6 +970,8 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, bidReq *ope func setDeviceImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest, ipValidtor iputil.IPValidator) { setIPImplicitly(httpReq, bidReq, ipValidtor) setUAImplicitly(httpReq, bidReq) + setDoNotTrackImplicitly(httpReq, bidReq) + } // setAuctionTypeImplicitly sets the auction type to 1 if it wasn't on the request, @@ -1192,6 +1200,24 @@ func setUAImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { } } +func setDoNotTrackImplicitly(httpReq *http.Request, bidReq *openrtb.BidRequest) { + if bidReq.Device == nil || bidReq.Device.DNT == nil { + dnt := httpReq.Header.Get(dntKey) + if dnt == "0" || dnt == "1" { + if bidReq.Device == nil { + bidReq.Device = &openrtb.Device{} + } + + switch dnt { + case "0": + bidReq.Device.DNT = &dntDisabled + case "1": + bidReq.Device.DNT = &dntEnabled + } + } + } +} + // parseUserID gets this user's ID for the host machine, if it exists. func parseUserID(cfg *config.Configuration, httpReq *http.Request) (string, bool) { if hostCookie, err := httpReq.Cookie(cfg.HostCookie.CookieName); hostCookie != nil && err == nil { diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index c697c206483..957760c61c9 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -604,6 +604,189 @@ func TestImplicitIPsEndToEnd(t *testing.T) { } } +func TestImplicitDNT(t *testing.T) { + var ( + disabled int8 = 0 + enabled int8 = 1 + ) + testCases := []struct { + description string + dntHeader string + request openrtb.BidRequest + expectedRequest openrtb.BidRequest + }{ + { + description: "Device Missing - Not Set In Header", + dntHeader: "", + request: openrtb.BidRequest{}, + expectedRequest: openrtb.BidRequest{}, + }, + { + description: "Device Missing - Set To 0 In Header", + dntHeader: "0", + request: openrtb.BidRequest{}, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &disabled, + }, + }, + }, + { + description: "Device Missing - Set To 1 In Header", + dntHeader: "1", + request: openrtb.BidRequest{}, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + }, + { + description: "Not Set In Request - Not Set In Header", + dntHeader: "", + request: openrtb.BidRequest{ + Device: &openrtb.Device{}, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{}, + }, + }, + { + description: "Not Set In Request - Set To 0 In Header", + dntHeader: "0", + request: openrtb.BidRequest{ + Device: &openrtb.Device{}, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &disabled, + }, + }, + }, + { + description: "Not Set In Request - Set To 1 In Header", + dntHeader: "1", + request: openrtb.BidRequest{ + Device: &openrtb.Device{}, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + }, + { + description: "Set In Request - Not Set In Header", + dntHeader: "", + request: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + }, + { + description: "Set In Request - Set To 0 In Header", + dntHeader: "0", + request: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + }, + { + description: "Set In Request - Set To 1 In Header", + dntHeader: "1", + request: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + expectedRequest: openrtb.BidRequest{ + Device: &openrtb.Device{ + DNT: &enabled, + }, + }, + }, + } + + for _, test := range testCases { + httpReq := httptest.NewRequest("POST", "/openrtb2/auction", nil) + httpReq.Header.Set("DNT", test.dntHeader) + setDoNotTrackImplicitly(httpReq, &test.request) + assert.Equal(t, test.expectedRequest, test.request) + } +} + +func TestImplicitDNTEndToEnd(t *testing.T) { + var ( + disabled int8 = 0 + enabled int8 = 1 + ) + testCases := []struct { + description string + reqJSONFile string + dntHeader string + expectedDNT *int8 + }{ + { + description: "Not Set In Request - Not Set In Header", + reqJSONFile: "site.json", + dntHeader: "", + expectedDNT: nil, + }, + { + description: "Not Set In Request - Set To 0 In Header", + reqJSONFile: "site.json", + dntHeader: "0", + expectedDNT: &disabled, + }, + { + description: "Not Set In Request - Set To 1 In Header", + reqJSONFile: "site.json", + dntHeader: "1", + expectedDNT: &enabled, + }, + { + description: "Set In Request - Not Set In Header", + reqJSONFile: "site-has-dnt.json", + dntHeader: "", + expectedDNT: &enabled, // Hardcoded value in test file. + }, + { + description: "Set In Request - Not Overwritten By Header", + reqJSONFile: "site-has-dnt.json", + dntHeader: "0", + expectedDNT: &enabled, // Hardcoded value in test file. + }, + } + + metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + for _, test := range testCases { + exchange := &nobidExchange{} + endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + + httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile))) + httpReq.Header.Set("DNT", test.dntHeader) + + endpoint(httptest.NewRecorder(), httpReq, nil) + + result := exchange.gotRequest + if !assert.NotEmpty(t, result, test.description+"Request received by the exchange.") { + t.FailNow() + } + assert.Equal(t, test.expectedDNT, result.Device.DNT, test.description+":dnt") + } +} func TestImplicitSecure(t *testing.T) { httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json"))) httpReq.Header.Set(http.CanonicalHeaderKey("X-Forwarded-Proto"), "https") diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-dnt.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-dnt.json new file mode 100644 index 00000000000..b1fae20afe4 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/site-has-dnt.json @@ -0,0 +1,45 @@ +{ + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "device": { + "dnt": 1 + }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "pmp": { + "deals": [ + { + "id": "some-deal-id" + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "pricegranularity": "low" + }, + "cache": { + "bids": {} + } + } + } + } + \ No newline at end of file From 55f4c453668d0b7233331067be7232b0e89f0626 Mon Sep 17 00:00:00 2001 From: Gena Date: Thu, 16 Jul 2020 17:22:23 +0300 Subject: [PATCH 142/318] Add video for InApp support (#1399) --- static/bidder-info/adtelligent.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/static/bidder-info/adtelligent.yaml b/static/bidder-info/adtelligent.yaml index fe791343daf..7a20d52b266 100644 --- a/static/bidder-info/adtelligent.yaml +++ b/static/bidder-info/adtelligent.yaml @@ -4,6 +4,7 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner From 62a72e23621b20ec3e5241ba955d36c775dfb394 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Thu, 16 Jul 2020 16:36:32 -0400 Subject: [PATCH 143/318] Timeout fix (#1390) --- config/util/loggers.go | 6 +-- exchange/bidder.go | 22 +++++++--- exchange/bidder_test.go | 91 +++++++++++++++++++++++++++++---------- exchange/exchange_test.go | 9 ++++ 4 files changed, 97 insertions(+), 31 deletions(-) diff --git a/config/util/loggers.go b/config/util/loggers.go index 88702e68763..d9aad43a7fb 100644 --- a/config/util/loggers.go +++ b/config/util/loggers.go @@ -4,18 +4,18 @@ import ( "math/rand" ) -type logMsg func(string, ...interface{}) +type LogMsg func(string, ...interface{}) type randomGenerator func() float32 // LogRandomSample will log a randam sample of the messages it is sent, based on the chance to log // chance = 1.0 => always log, // chance = 0.0 => never log -func LogRandomSample(msg string, logger logMsg, chance float32) { +func LogRandomSample(msg string, logger LogMsg, chance float32) { logRandomSampleImpl(msg, logger, chance, rand.Float32) } -func logRandomSampleImpl(msg string, logger logMsg, chance float32, randGenerator randomGenerator) { +func logRandomSampleImpl(msg string, logger LogMsg, chance float32, randGenerator randomGenerator) { if chance < 1.0 && randGenerator() > chance { // this is the chance we don't log anything return diff --git a/exchange/bidder.go b/exchange/bidder.go index df9f0a3bf1b..ee6a4942147 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -312,6 +312,10 @@ func makeExt(httpInfo *httpCallInfo) *openrtb_ext.ExtHttpCall { // doRequest makes a request, handles the response, and returns the data needed by the // Bidder interface. func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.RequestData) *httpCallInfo { + return bidder.doRequestImpl(ctx, req, glog.Warningf) +} + +func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.RequestData, logger util.LogMsg) *httpCallInfo { httpReq, err := http.NewRequest(req.Method, req.Uri, bytes.NewBuffer(req.Body)) if err != nil { return &httpCallInfo{ @@ -325,12 +329,18 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques if err != nil { if err == context.DeadlineExceeded { err = &errortypes.Timeout{Message: err.Error()} - if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); ok { + var corebidder adapters.Bidder = bidder.Bidder + // The bidder adapter normally stores an info-aware bidder (a bidder wrapper) + // rather than the actual bidder. So we need to unpack that first. + if b, ok := corebidder.(*adapters.InfoAwareBidder); ok { + corebidder = b.Bidder + } + if tb, ok := corebidder.(adapters.TimeoutBidder); ok { // Toss the timeout notification call into a go routine, as we are out of time' // and cannot delay processing. We don't do anything result, as there is not much // we can do about a timeout notification failure. We do not want to get stuck in // a loop of trying to report timeouts to the timeout notifications. - go bidder.doTimeoutNotification(tb, req) + go bidder.doTimeoutNotification(tb, req, logger) } } @@ -366,7 +376,7 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques } } -func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.TimeoutBidder, req *adapters.RequestData) { +func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.TimeoutBidder, req *adapters.RequestData, logger util.LogMsg) { ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) defer cancel() toReq, errL := timeoutBidder.MakeTimeoutNotification(req) @@ -385,13 +395,13 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou msg = fmt.Sprintf("TimeoutNotification: error:(%s) body:%s", err.Error(), string(toReq.Body)) } // If logging is turned on, and logging is not disallowed via FailOnly - util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) } } else { bidder.me.RecordTimeoutNotice(false) if bidder.DebugConfig.TimeoutNotification.Log { msg := fmt.Sprintf("TimeoutNotification: Failed to make timeout request: method(%s), uri(%s), error(%s)", toReq.Method, toReq.Uri, err.Error()) - util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) } } } else if bidder.DebugConfig.TimeoutNotification.Log { @@ -402,7 +412,7 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou } else { msg = fmt.Sprintf("TimeoutNotification: Failed to generate timeout request: error(%s), bidder request marshal failed(%s)", errL[0].Error(), err.Error()) } - util.LogRandomSample(msg, glog.Warningf, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) } } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index b776715adaf..d4fc0cf7cd3 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -1,6 +1,7 @@ package exchange import ( + "bytes" "context" "encoding/json" "errors" @@ -10,6 +11,7 @@ import ( "testing" "time" + "github.com/golang/glog" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/config" @@ -1237,8 +1239,8 @@ func TestTimeoutNotificationOff(t *testing.T) { server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) defer server.Close() - bidderImpl := ¬ifingBidder{ - notiRequest: adapters.RequestData{ + bidderImpl := ¬ifyingBidder{ + notifyRequest: adapters.RequestData{ Method: "GET", Uri: server.URL + "/notify/me", Body: nil, @@ -1254,39 +1256,83 @@ func TestTimeoutNotificationOff(t *testing.T) { if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok { t.Error("Failed to cast bidder to a TimeoutBidder") } else { - bidder.doTimeoutNotification(tb, &adapters.RequestData{}) + bidder.doTimeoutNotification(tb, &adapters.RequestData{}, glog.Warningf) } } func TestTimeoutNotificationOn(t *testing.T) { - respBody := "{\"bid\":false}" - respStatus := 200 - server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) + // Expire context immediately to force timeout handler. + ctx, cancelFunc := context.WithDeadline(context.Background(), time.Now()) + cancelFunc() + + // Notification logic is hardcoded for 200ms. We need to wait for a little longer than that. + server := httptest.NewServer(mockSlowHandler(205*time.Millisecond, 200, `{"bid":false}`)) defer server.Close() - bidderImpl := ¬ifingBidder{ - notiRequest: adapters.RequestData{ + bidder := ¬ifyingBidder{ + notifyRequest: adapters.RequestData{ Method: "GET", Uri: server.URL + "/notify/me", Body: nil, Headers: http.Header{}, }, } - bidder := &bidderAdapter{ - Bidder: bidderImpl, + + // Wrap with BidderInfo to mimic exchange.go flow. + bidderWrappedWithInfo := wrapWithBidderInfo(bidder) + + bidderAdapter := &bidderAdapter{ + Bidder: bidderWrappedWithInfo, Client: server.Client(), DebugConfig: config.Debug{ TimeoutNotification: config.TimeoutNotification{ - Log: true, + Log: true, + SamplingRate: 1.0, }, }, me: &metricsConfig.DummyMetricsEngine{}, } - if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok { - t.Error("Failed to cast bidder to a TimeoutBidder") - } else { - bidder.doTimeoutNotification(tb, &adapters.RequestData{}) + + // Unwrap To Mimic exchange.go Casting Code + var coreBidder adapters.Bidder = bidderAdapter.Bidder + if b, ok := coreBidder.(*adapters.InfoAwareBidder); ok { + coreBidder = b.Bidder + } + if _, ok := coreBidder.(adapters.TimeoutBidder); !ok { + t.Fatal("Failed to cast bidder to a TimeoutBidder") + } + + bidRequest := adapters.RequestData{ + Method: "POST", + Uri: server.URL, + Body: []byte(`{"id":"this-id","app":{"publisher":{"id":"pub-id"}}}`), + } + + var loggerBuffer bytes.Buffer + logger := func(msg string, args ...interface{}) { + loggerBuffer.WriteString(fmt.Sprintf(fmt.Sprintln(msg), args...)) + } + + bidderAdapter.doRequestImpl(ctx, &bidRequest, logger) + + // Wait a little longer than the 205ms mock server sleep. + time.Sleep(210 * time.Millisecond) + + logExpected := "TimeoutNotification: error:(context deadline exceeded) body:\n" + logActual := loggerBuffer.String() + assert.EqualValues(t, logExpected, logActual) +} + +func wrapWithBidderInfo(bidder adapters.Bidder) adapters.Bidder { + bidderInfo := adapters.BidderInfo{ + Status: adapters.StatusActive, + Capabilities: &adapters.CapabilitiesInfo{ + App: &adapters.PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}, + }, + }, } + return adapters.EnforceBidderInfo(bidder, bidderInfo) } type goodSingleBidder struct { @@ -1363,18 +1409,19 @@ func (bidder *bidRejector) MakeBids(internalRequest *openrtb.BidRequest, externa return nil, []error{errors.New("Can't make a response.")} } -type notifingBidder struct { - notiRequest adapters.RequestData +type notifyingBidder struct { + requests []*adapters.RequestData + notifyRequest adapters.RequestData } -func (bidder *notifingBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - return nil, nil +func (bidder *notifyingBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + return bidder.requests, nil } -func (bidder *notifingBidder) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { +func (bidder *notifyingBidder) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { return nil, nil } -func (bidder *notifingBidder) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error) { - return &bidder.notiRequest, nil +func (bidder *notifyingBidder) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.RequestData, []error) { + return &bidder.notifyRequest, nil } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 161b24fd1c1..96f740de23a 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1830,6 +1830,15 @@ func mockHandler(statusCode int, getBody string, postBody string) http.Handler { }) } +func mockSlowHandler(delay time.Duration, statusCode int, body string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(delay) + + w.WriteHeader(statusCode) + w.Write([]byte(body)) + }) +} + type wellBehavedCache struct{} func (c *wellBehavedCache) GetExtCacheData() (string, string) { From 5a7a2cf17b12f6ad78889c31cf1bdf7d7c71c904 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Fri, 17 Jul 2020 09:59:59 -0400 Subject: [PATCH 144/318] Privacy Request Metrics (#1400) * Privacy Request Metrics * Fix Bug + Add Unit Tests * Fixed Tests * Fix Typo --- exchange/exchange.go | 7 +- exchange/utils.go | 22 ++- exchange/utils_test.go | 198 ++++++++++++++++++++--- gdpr/impl_test.go | 2 +- pbsmetrics/config/metrics.go | 10 +- pbsmetrics/go_metrics.go | 49 ++++-- pbsmetrics/go_metrics_test.go | 65 +++++++- pbsmetrics/metrics.go | 14 +- pbsmetrics/metrics_mock.go | 6 +- pbsmetrics/prometheus/preload.go | 22 ++- pbsmetrics/prometheus/prometheus.go | 56 ++++++- pbsmetrics/prometheus/prometheus_test.go | 85 ++++++++-- 12 files changed, 454 insertions(+), 82 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index 174a0b3e0fc..3f0258dd3c1 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -104,11 +104,10 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, cleanMetrics, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + + e.me.RecordRequestPrivacy(privacyLabels) - if cleanMetrics.gdprEnforced { - e.me.RecordTCFReq(pbsmetrics.TCFVersionToValue(cleanMetrics.gdprTcfVersion)) - } // List of bidders we have requests for. liveAdapters := listBiddersWithRequests(cleanRequests) diff --git a/exchange/utils.go b/exchange/utils.go index 4de985eca40..bc1b555e507 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -19,15 +19,6 @@ import ( "github.com/prebid/prebid-server/privacy/lmt" ) -// cleanMetrics is a struct to export any metrics data resulting from cleanOpenRTBRequests(). It starts with just -// the TCF version, but made a struct to facilitate future expansion -type cleanMetrics struct { - // A simple flag if GDPR is being enforced on this request. - gdprEnforced bool - // a zero value means a missing or invalid GDPR string - gdprTcfVersion int -} - func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext.ExtRequestPrebidSChainSChain, error) { bidderToSChains := make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) @@ -63,7 +54,7 @@ func cleanOpenRTBRequests(ctx context.Context, labels pbsmetrics.Labels, gDPR gdpr.Permissions, usersyncIfAmbiguous bool, - privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, cleanMetrics cleanMetrics, errs []error) { + privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, privacyLabels pbsmetrics.PrivacyLabels, errs []error) { impsByBidder, errs := splitImps(orig.Imp) if len(errs) > 0 { @@ -98,13 +89,20 @@ func cleanOpenRTBRequests(ctx context.Context, LMT: lmtPolicy.ShouldEnforce(), } + privacyLabels.CCPAProvided = ccpaPolicy.Value != "" + privacyLabels.CCPAEnforced = privacyEnforcement.CCPA + privacyLabels.COPPAEnforced = privacyEnforcement.COPPA + privacyLabels.LMTEnforced = privacyEnforcement.LMT + if gdpr == 1 { - cleanMetrics.gdprEnforced = true + privacyLabels.GDPREnforced = true parsedConsent, err := vendorconsent.ParseString(consent) if err == nil { - cleanMetrics.gdprTcfVersion = int(parsedConsent.Version()) + version := int(parsedConsent.Version()) + privacyLabels.GDPRTCFVersion = pbsmetrics.TCFVersionToValue(version) } } + // bidder level privacy policies for bidder, bidReq := range requestsByBidder { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 6d66e816e7b..608e6a17a10 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -15,7 +15,9 @@ import ( // permissionsMock mocks the Permissions interface for tests // // It only allows appnexus for GDPR consent -type permissionsMock struct{} +type permissionsMock struct { + personalInfoAllowed bool +} func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { return true, nil @@ -26,10 +28,7 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ } func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - if bidder == "appnexus" { - return true, true, nil - } - return false, false, nil + return p.personalInfoAllowed, p.personalInfoAllowed, nil } func (p *permissionsMock) AMPException() bool { @@ -80,7 +79,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { } for _, test := range testCases { - reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -92,26 +91,48 @@ func TestCleanOpenRTBRequests(t *testing.T) { func TestCleanOpenRTBRequestsCCPA(t *testing.T) { testCases := []struct { - description string - enforceCCPA bool - expectDataScrub bool + description string + ccpaConsent string + enforceCCPA bool + expectDataScrub bool + expectPrivacyLabels pbsmetrics.PrivacyLabels }{ { - description: "Feature Flag Enabled", + description: "Feature Flag Enabled - Opt Out", + ccpaConsent: "1-Y-", enforceCCPA: true, expectDataScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: true, + CCPAEnforced: true, + }, + }, + { + description: "Feature Flag Enabled - Opt In", + ccpaConsent: "1-N-", + enforceCCPA: true, + expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: true, + CCPAEnforced: false, + }, }, { description: "Feature Flag Disabled", + ccpaConsent: "1-Y-", enforceCCPA: false, expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: false, + CCPAEnforced: false, + }, }, } for _, test := range testCases { req := newBidRequest(t) req.Regs = &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"1-Y-"}`), + Ext: json.RawMessage(`{"us_privacy":"` + test.ccpaConsent + `"}`), } privacyConfig := config.Privacy{ @@ -120,11 +141,10 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { }, } - results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) - if test.expectDataScrub { assert.Equal(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") assert.Equal(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") @@ -132,6 +152,51 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { assert.NotEqual(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") assert.NotEqual(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") } + assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") + } +} + +func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { + testCases := []struct { + description string + coppa int8 + expectDataScrub bool + expectPrivacyLabels pbsmetrics.PrivacyLabels + }{ + { + description: "Enabled", + coppa: 1, + expectDataScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + COPPAEnforced: true, + }, + }, + { + description: "Disabled", + coppa: 0, + expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + COPPAEnforced: false, + }, + }, + } + + for _, test := range testCases { + req := newBidRequest(t) + req.Regs = &openrtb.Regs{COPPA: test.coppa} + + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}) + result := results["appnexus"] + + assert.Nil(t, errs) + if test.expectDataScrub { + assert.Equal(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.Equal(t, result.User.Yob, int64(0), test.description+":User.Yob") + } else { + assert.NotEqual(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.NotEqual(t, result.User.Yob, int64(0), test.description+":User.Yob") + } + assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") } } @@ -227,34 +292,47 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { disabled int8 = 0 ) testCases := []struct { - description string - lmt *int8 - enforceLMT bool - expectDataScrub bool + description string + lmt *int8 + enforceLMT bool + expectDataScrub bool + expectPrivacyLabels pbsmetrics.PrivacyLabels }{ { description: "Feature Flag Enabled - OpenTRB Enabled", lmt: &enabled, enforceLMT: true, expectDataScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + LMTEnforced: true, + }, }, { description: "Feature Flag Disabled - OpenTRB Enabled", lmt: &enabled, enforceLMT: false, expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + LMTEnforced: false, + }, }, { description: "Feature Flag Enabled - OpenTRB Disabled", lmt: &disabled, enforceLMT: true, expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + LMTEnforced: false, + }, }, { description: "Feature Flag Disabled - OpenTRB Disabled", lmt: &disabled, enforceLMT: false, expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + LMTEnforced: false, + }, }, } @@ -268,11 +346,10 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { }, } - results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) - if test.expectDataScrub { assert.Equal(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") assert.Equal(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") @@ -280,6 +357,88 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { assert.NotEqual(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") assert.NotEqual(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") } + assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") + } +} + +func TestCleanOpenRTBRequestsGDPR(t *testing.T) { + testCases := []struct { + description string + gdpr string + gdprConsent string + gdprScrub bool + enforceGDPR bool + expectPrivacyLabels pbsmetrics.PrivacyLabels + }{ + { + description: "Enforce - TCF Invalid", + gdpr: "1", + gdprConsent: "malformed", + gdprScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: "", + }, + }, + { + description: "Enforce - TCF 1", + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV1, + }, + }, + { + description: "Enforce - TCF 2", + gdpr: "1", + gdprConsent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + gdprScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV2, + }, + }, + { + description: "Not Enforce - TCF 1", + gdpr: "0", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: false, + GDPRTCFVersion: "", + }, + }, + } + + for _, test := range testCases { + req := newBidRequest(t) + req.User.Ext = json.RawMessage(`{"consent":"` + test.gdprConsent + `"}`) + req.Regs = &openrtb.Regs{ + Ext: json.RawMessage(`{"gdpr":` + test.gdpr + `}`), + } + + privacyConfig := config.Privacy{ + GDPR: config.GDPR{ + TCF2: config.TCF2{ + Enabled: true, + }, + }, + } + + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: !test.gdprScrub}, true, privacyConfig) + result := results["appnexus"] + + assert.Nil(t, errs) + if test.gdprScrub { + assert.Equal(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.Equal(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") + } else { + assert.NotEqual(t, result.User.BuyerUID, "", test.description+":User.BuyerUID") + assert.NotEqual(t, result.Device.DIDMD5, "", test.description+":Device.DIDMD5") + } + assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") } } @@ -352,6 +511,7 @@ func newBidRequest(t *testing.T) *openrtb.BidRequest { User: &openrtb.User{ ID: "our-id", BuyerUID: "their-id", + Yob: 1982, Ext: json.RawMessage(`{"digitrust":{"id":"digi-id","keyv":1,"pref":1}}`), }, Imp: []openrtb.Imp{{ diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index f05f25e87ea..05b2fb6d98e 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -276,7 +276,7 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { }, } - // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes and vendors 2, 6, 8 // PI needs all purposes to succeed testDefs := []tcf2TestDef{ { diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go index 3d105dead44..0dbe9a69d9f 100644 --- a/pbsmetrics/config/metrics.go +++ b/pbsmetrics/config/metrics.go @@ -195,10 +195,10 @@ func (me *MultiMetricsEngine) RecordTimeoutNotice(success bool) { } } -// RecordTCFReq across all engines -func (me *MultiMetricsEngine) RecordTCFReq(version pbsmetrics.TCFVersionValue) { +// RecordRequestPrivacy across all engines +func (me *MultiMetricsEngine) RecordRequestPrivacy(privacy pbsmetrics.PrivacyLabels) { for _, thisME := range *me { - thisME.RecordTCFReq(version) + thisME.RecordRequestPrivacy(privacy) } } @@ -281,6 +281,6 @@ func (me *DummyMetricsEngine) RecordRequestQueueTime(success bool, requestType p func (me *DummyMetricsEngine) RecordTimeoutNotice(success bool) { } -// RecordReq as a noop -func (me *DummyMetricsEngine) RecordTCFReq(version pbsmetrics.TCFVersionValue) { +// RecordRequestPrivacy as a noop +func (me *DummyMetricsEngine) RecordRequestPrivacy(privacy pbsmetrics.PrivacyLabels) { } diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go index 73eb30a1504..836434bf25e 100644 --- a/pbsmetrics/go_metrics.go +++ b/pbsmetrics/go_metrics.go @@ -53,7 +53,11 @@ type Metrics struct { TimeoutNotificationFailure metrics.Meter // TCF adaption metrics - TCFReqVersion map[TCFVersionValue]metrics.Meter + PrivacyCCPARequest metrics.Meter + PrivacyCCPARequestOptOut metrics.Meter + PrivacyCOPPARequest metrics.Meter + PrivacyLMTRequest metrics.Meter + PrivacyTCFRequestVersion map[TCFVersionValue]metrics.Meter AdapterMetrics map[openrtb_ext.BidderName]*AdapterMetrics // Don't export accountMetrics because we need helper functions here to insure its properly populated dynamically @@ -141,7 +145,11 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa TimeoutNotificationSuccess: blankMeter, TimeoutNotificationFailure: blankMeter, - TCFReqVersion: make(map[TCFVersionValue]metrics.Meter, len(TCFVersions())), + PrivacyCCPARequest: blankMeter, + PrivacyCCPARequestOptOut: blankMeter, + PrivacyCOPPARequest: blankMeter, + PrivacyLMTRequest: blankMeter, + PrivacyTCFRequestVersion: make(map[TCFVersionValue]metrics.Meter, len(TCFVersions())), AdapterMetrics: make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)), accountMetrics: make(map[string]*accountMetrics), @@ -149,6 +157,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa exchanges: exchanges, } + for _, a := range exchanges { newMetrics.AdapterMetrics[a] = makeBlankAdapterMetrics() } @@ -166,7 +175,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa } for _, v := range TCFVersions() { - newMetrics.TCFReqVersion[v] = blankMeter + newMetrics.PrivacyTCFRequestVersion[v] = blankMeter } //to minimize memory usage, queuedTimeout metric is now supported for video endpoint only @@ -234,8 +243,12 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d newMetrics.TimeoutNotificationSuccess = metrics.GetOrRegisterMeter("timeout_notification.ok", registry) newMetrics.TimeoutNotificationFailure = metrics.GetOrRegisterMeter("timeout_notification.failed", registry) + newMetrics.PrivacyCCPARequest = metrics.GetOrRegisterMeter("privacy.request.ccpa.specified", registry) + newMetrics.PrivacyCCPARequestOptOut = metrics.GetOrRegisterMeter("privacy.request.ccpa.opt-out", registry) + newMetrics.PrivacyCOPPARequest = metrics.GetOrRegisterMeter("privacy.request.coppa", registry) + newMetrics.PrivacyLMTRequest = metrics.GetOrRegisterMeter("privacy.request.lmt", registry) for _, version := range TCFVersions() { - newMetrics.TCFReqVersion[version] = metrics.GetOrRegisterMeter(fmt.Sprintf("privacy.request.tcf.%s", string(version)), registry) + newMetrics.PrivacyTCFRequestVersion[version] = metrics.GetOrRegisterMeter(fmt.Sprintf("privacy.request.tcf.%s", string(version)), registry) } return newMetrics @@ -582,12 +595,28 @@ func (me *Metrics) RecordTimeoutNotice(success bool) { return } -func (me *Metrics) RecordTCFReq(version TCFVersionValue) { - met, ok := me.TCFReqVersion[version] - if ok { - met.Mark(1) - } else { - me.TCFReqVersion[TCFVersionErr].Mark(1) +func (me *Metrics) RecordRequestPrivacy(privacy PrivacyLabels) { + if privacy.CCPAProvided { + me.PrivacyCCPARequest.Mark(1) + if privacy.CCPAEnforced { + me.PrivacyCCPARequestOptOut.Mark(1) + } + } + + if privacy.COPPAEnforced { + me.PrivacyCOPPARequest.Mark(1) + } + + if privacy.GDPREnforced { + if metric, ok := me.PrivacyTCFRequestVersion[privacy.GDPRTCFVersion]; ok { + metric.Mark(1) + } else { + me.PrivacyTCFRequestVersion[TCFVersionErr].Mark(1) + } + } + + if privacy.LMTEnforced { + me.PrivacyLMTRequest.Mark(1) } return } diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go index 6d9eaf9f0e9..2faa08491e0 100644 --- a/pbsmetrics/go_metrics_test.go +++ b/pbsmetrics/go_metrics_test.go @@ -56,10 +56,14 @@ func TestNewMetrics(t *testing.T) { ensureContains(t, registry, "timeout_notification.ok", m.TimeoutNotificationSuccess) ensureContains(t, registry, "timeout_notification.failed", m.TimeoutNotificationFailure) - ensureContains(t, registry, "privacy.request.tcf.v1", m.TCFReqVersion[TCFVersionV1]) - ensureContains(t, registry, "privacy.request.tcf.v2", m.TCFReqVersion[TCFVersionV2]) - ensureContains(t, registry, "privacy.request.tcf.err", m.TCFReqVersion[TCFVersionErr]) + ensureContains(t, registry, "privacy.request.ccpa.specified", m.PrivacyCCPARequest) + ensureContains(t, registry, "privacy.request.ccpa.opt-out", m.PrivacyCCPARequestOptOut) + ensureContains(t, registry, "privacy.request.coppa", m.PrivacyCOPPARequest) + ensureContains(t, registry, "privacy.request.lmt", m.PrivacyLMTRequest) + ensureContains(t, registry, "privacy.request.tcf.v1", m.PrivacyTCFRequestVersion[TCFVersionV1]) + ensureContains(t, registry, "privacy.request.tcf.v2", m.PrivacyTCFRequestVersion[TCFVersionV2]) + ensureContains(t, registry, "privacy.request.tcf.err", m.PrivacyTCFRequestVersion[TCFVersionErr]) } func TestRecordBidType(t *testing.T) { @@ -202,6 +206,61 @@ func TestRecordPrebidCacheRequestTimeWithNotSuccess(t *testing.T) { assert.Equal(t, m.PrebidCacheRequestTimerError.Count(), int64(1)) } +func TestRecordRequestPrivacy(t *testing.T) { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true}) + + // CCPA + m.RecordRequestPrivacy(PrivacyLabels{ + CCPAEnforced: true, + CCPAProvided: true, + }) + m.RecordRequestPrivacy(PrivacyLabels{ + CCPAEnforced: true, + CCPAProvided: false, + }) + m.RecordRequestPrivacy(PrivacyLabels{ + CCPAEnforced: false, + CCPAProvided: true, + }) + + // COPPA + m.RecordRequestPrivacy(PrivacyLabels{ + COPPAEnforced: true, + }) + + // LMT + m.RecordRequestPrivacy(PrivacyLabels{ + LMTEnforced: true, + }) + + // GDPR + m.RecordRequestPrivacy(PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: TCFVersionErr, + }) + m.RecordRequestPrivacy(PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: TCFVersionV1, + }) + m.RecordRequestPrivacy(PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: TCFVersionV2, + }) + m.RecordRequestPrivacy(PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: TCFVersionV1, + }) + + assert.Equal(t, m.PrivacyCCPARequest.Count(), int64(2), "CCPA") + assert.Equal(t, m.PrivacyCCPARequestOptOut.Count(), int64(1), "CCPA Opt Out") + assert.Equal(t, m.PrivacyCOPPARequest.Count(), int64(1), "COPPA") + assert.Equal(t, m.PrivacyLMTRequest.Count(), int64(1), "LMT") + assert.Equal(t, m.PrivacyTCFRequestVersion[TCFVersionErr].Count(), int64(1), "TCF Err") + assert.Equal(t, m.PrivacyTCFRequestVersion[TCFVersionV1].Count(), int64(2), "TCF V1") + assert.Equal(t, m.PrivacyTCFRequestVersion[TCFVersionV2].Count(), int64(1), "TCF V2") +} + func ensureContainsBidTypeMetrics(t *testing.T, registry metrics.Registry, prefix string, mdm map[openrtb_ext.BidType]*MarkupDeliveryMetrics) { ensureContains(t, registry, prefix+".banner.adm_bids_received", mdm[openrtb_ext.BidTypeBanner].AdmMeter) ensureContains(t, registry, prefix+".banner.nurl_bids_received", mdm[openrtb_ext.BidTypeBanner].NurlMeter) diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index 0e94fe71e90..514fbac1015 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -41,6 +41,16 @@ type RequestLabels struct { RequestStatus RequestStatus } +// PrivacyLabels defines metrics describing the result of privacy enforcement. +type PrivacyLabels struct { + CCPAEnforced bool + CCPAProvided bool + COPPAEnforced bool + GDPREnforced bool + GDPRTCFVersion TCFVersionValue + LMTEnforced bool +} + // Label typecasting. Se below the type definitions for possible values // DemandSource : Demand source enumeration @@ -257,7 +267,7 @@ const ( TCFVersionV2 TCFVersionValue = "v2" ) -// TCFVersions rtuens the possible values for the TCF version +// TCFVersions returns the possible values for the TCF version func TCFVersions() []TCFVersionValue { return []TCFVersionValue{ TCFVersionErr, @@ -305,5 +315,5 @@ type MetricsEngine interface { RecordPrebidCacheRequestTime(success bool, length time.Duration) RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) RecordTimeoutNotice(sucess bool) - RecordTCFReq(version TCFVersionValue) + RecordRequestPrivacy(privacy PrivacyLabels) } diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go index a6d36a72401..6c263f0af4d 100644 --- a/pbsmetrics/metrics_mock.go +++ b/pbsmetrics/metrics_mock.go @@ -107,7 +107,7 @@ func (me *MetricsEngineMock) RecordTimeoutNotice(success bool) { me.Called(success) } -// RecordTCFReq mock -func (me *MetricsEngineMock) RecordTCFReq(version TCFVersionValue) { - me.Called(version) +// RecordRequestPrivacy mock +func (me *MetricsEngineMock) RecordRequestPrivacy(privacy PrivacyLabels) { + me.Called(privacy) } diff --git a/pbsmetrics/prometheus/preload.go b/pbsmetrics/prometheus/preload.go index 19f4f225af9..ef1d300c4df 100644 --- a/pbsmetrics/prometheus/preload.go +++ b/pbsmetrics/prometheus/preload.go @@ -8,15 +8,16 @@ import ( func preloadLabelValues(m *Metrics) { var ( actionValues = actionsAsString() - adapterValues = adaptersAsString() adapterErrorValues = adapterErrorsAsString() + adapterValues = adaptersAsString() bidTypeValues = []string{markupDeliveryAdm, markupDeliveryNurl} boolValues = boolValuesAsString() cacheResultValues = cacheResultsAsString() - cookieValues = cookieTypesAsString() connectionErrorValues = []string{connectionAcceptError, connectionCloseError} + cookieValues = cookieTypesAsString() requestStatusValues = requestStatusesAsString() requestTypeValues = requestTypesAsString() + sourceValues = []string{sourceRequest} ) preloadLabelValuesForCounter(m.connectionsError, map[string][]string{ @@ -100,9 +101,22 @@ func preloadLabelValues(m *Metrics) { requestStatusLabel: {requestSuccessLabel, requestRejectLabel}, }) - preloadLabelValuesForCounter(m.tcfVersion, map[string][]string{ + preloadLabelValuesForCounter(m.privacyCCPA, map[string][]string{ + sourceLabel: sourceValues, + optOutLabel: boolValues, + }) + + preloadLabelValuesForCounter(m.privacyCOPPA, map[string][]string{ + sourceLabel: sourceValues, + }) + + preloadLabelValuesForCounter(m.privacyLMT, map[string][]string{ + sourceLabel: sourceValues, + }) + + preloadLabelValuesForCounter(m.privacyTCF, map[string][]string{ + sourceLabel: sourceValues, versionLabel: tcfVersionsAsString(), - sourceLabel: {string(sourceRequest)}, }) } diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index bf854746fd2..d94c4d78f62 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -29,7 +29,10 @@ type Metrics struct { storedImpressionsCacheResult *prometheus.CounterVec storedRequestCacheResult *prometheus.CounterVec timeoutNotifications *prometheus.CounterVec - tcfVersion *prometheus.CounterVec + privacyCCPA *prometheus.CounterVec + privacyCOPPA *prometheus.CounterVec + privacyLMT *prometheus.CounterVec + privacyTCF *prometheus.CounterVec // Adapter Metrics adapterBids *prometheus.CounterVec @@ -60,6 +63,7 @@ const ( isNativeLabel = "native" isVideoLabel = "video" markupDeliveryLabel = "delivery" + optOutLabel = "opt_out" privacyBlockedLabel = "privacy_blocked" requestStatusLabel = "request_status" requestTypeLabel = "request_type" @@ -165,11 +169,26 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of timeout notifications triggered, and if they were successfully sent.", []string{successLabel}) - metrics.tcfVersion = newCounter(cfg, metrics.Registry, + metrics.privacyCCPA = newCounter(cfg, metrics.Registry, + "privacy_ccpa", + "Count of total requests to Prebid Server where CCPA was provided by source and opt-out .", + []string{sourceLabel, optOutLabel}) + + metrics.privacyCOPPA = newCounter(cfg, metrics.Registry, + "privacy_coppa", + "Count of total requests to Prebid Server where the COPPA flag was set by source", + []string{sourceLabel}) + + metrics.privacyTCF = newCounter(cfg, metrics.Registry, "privacy_tcf", - "Count of TCF versions for requests where GDPR was enforced.", + "Count of TCF versions for requests where GDPR was enforced by source and version.", []string{versionLabel, sourceLabel}) + metrics.privacyLMT = newCounter(cfg, metrics.Registry, + "privacy_lmt", + "Count of total requests to Prebid Server where the LMT flag was set by source", + []string{sourceLabel}) + metrics.adapterBids = newCounter(cfg, metrics.Registry, "adapter_bids", "Count of bids labeled by adapter and markup delivery type (adm or nurl).", @@ -434,9 +453,30 @@ func (m *Metrics) RecordTimeoutNotice(success bool) { } } -func (m *Metrics) RecordTCFReq(version pbsmetrics.TCFVersionValue) { - m.tcfVersion.With(prometheus.Labels{ - versionLabel: string(version), - sourceLabel: sourceRequest, - }).Inc() +func (m *Metrics) RecordRequestPrivacy(privacy pbsmetrics.PrivacyLabels) { + if privacy.CCPAProvided { + m.privacyCCPA.With(prometheus.Labels{ + sourceLabel: sourceRequest, + optOutLabel: strconv.FormatBool(privacy.CCPAEnforced), + }).Inc() + } + + if privacy.COPPAEnforced { + m.privacyCOPPA.With(prometheus.Labels{ + sourceLabel: sourceRequest, + }).Inc() + } + + if privacy.GDPREnforced { + m.privacyTCF.With(prometheus.Labels{ + versionLabel: string(privacy.GDPRTCFVersion), + sourceLabel: sourceRequest, + }).Inc() + } + + if privacy.LMTEnforced { + m.privacyLMT.With(prometheus.Labels{ + sourceLabel: sourceRequest, + }).Inc() + } } diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index 03daff0d56b..b722ab28b5c 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -944,33 +944,96 @@ func TestTimeoutNotifications(t *testing.T) { } -func TestTCFMetrics(t *testing.T) { +func TestRecordRequestPrivacy(t *testing.T) { m := createMetricsForTesting() - m.RecordTCFReq(pbsmetrics.TCFVersionToValue(0)) - m.RecordTCFReq(pbsmetrics.TCFVersionToValue(1)) - m.RecordTCFReq(pbsmetrics.TCFVersionToValue(2)) - m.RecordTCFReq(pbsmetrics.TCFVersionToValue(1)) + // CCPA + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + CCPAEnforced: true, + CCPAProvided: true, + }) + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + CCPAEnforced: true, + CCPAProvided: false, + }) + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + CCPAEnforced: false, + CCPAProvided: true, + }) + + // COPPA + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + COPPAEnforced: true, + }) - assertCounterVecValue(t, "", "privacy_tcf:err", m.tcfVersion, + // LMT + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + LMTEnforced: true, + }) + + // GDPR + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionErr, + }) + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV1, + }) + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV2, + }) + m.RecordRequestPrivacy(pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV1, + }) + + assertCounterVecValue(t, "", "privacy_ccpa", m.privacyCCPA, + float64(1), + prometheus.Labels{ + sourceLabel: sourceRequest, + optOutLabel: "true", + }) + + assertCounterVecValue(t, "", "privacy_ccpa", m.privacyCCPA, + float64(1), + prometheus.Labels{ + sourceLabel: sourceRequest, + optOutLabel: "false", + }) + + assertCounterVecValue(t, "", "privacy_coppa", m.privacyCOPPA, + float64(1), + prometheus.Labels{ + sourceLabel: sourceRequest, + }) + + assertCounterVecValue(t, "", "privacy_lmt", m.privacyLMT, + float64(1), + prometheus.Labels{ + sourceLabel: sourceRequest, + }) + + assertCounterVecValue(t, "", "privacy_tcf:err", m.privacyTCF, float64(1), prometheus.Labels{ - versionLabel: "err", sourceLabel: sourceRequest, + versionLabel: "err", }) - assertCounterVecValue(t, "", "privacy_tcf:v1", m.tcfVersion, + assertCounterVecValue(t, "", "privacy_tcf:v1", m.privacyTCF, float64(2), prometheus.Labels{ - versionLabel: "v1", sourceLabel: sourceRequest, + versionLabel: "v1", }) - assertCounterVecValue(t, "", "privacy_tcf:v2", m.tcfVersion, + assertCounterVecValue(t, "", "privacy_tcf:v2", m.privacyTCF, float64(1), prometheus.Labels{ - versionLabel: "v2", sourceLabel: sourceRequest, + versionLabel: "v2", }) } From 0ccb77388da8d96fe6e1ee512d17fa415e607c70 Mon Sep 17 00:00:00 2001 From: Daniel Barrigas Date: Fri, 17 Jul 2020 16:32:22 +0100 Subject: [PATCH 145/318] Parse Site.Publisher.ID from Amp Auction HTTP Req Query Parameter "account" (#1403) --- endpoints/openrtb2/amp_auction.go | 8 ++++++++ endpoints/openrtb2/amp_auction_test.go | 10 ++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index e8b5d3ecc76..8efba5a926c 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -388,6 +388,14 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope setAmpExt(req.Site, "1") + account := httpRequest.FormValue("account") + if account != "" { + if req.Site.Publisher == nil { + req.Site.Publisher = &openrtb.Publisher{} + } + req.Site.Publisher.ID = account + } + slot := httpRequest.FormValue("slot") if slot != "" { req.Imp[0].TagID = slot diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 731fd55e196..692d3fb0c5d 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -755,8 +755,9 @@ func TestQueryParamOverrides(t *testing.T) { curl := "http://example.com" slot := "1234" timeout := int64(500) + account := "12345" - request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s&debug=1&curl=%s&slot=%s&timeout=%d", requestID, curl, slot, timeout), nil) + request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s&debug=1&curl=%s&slot=%s&timeout=%d&account=%s", requestID, curl, slot, timeout, account), nil) recorder := httptest.NewRecorder() endpoint(recorder, request, nil) @@ -784,6 +785,10 @@ func TestQueryParamOverrides(t *testing.T) { if resolvedRequest.Site == nil || resolvedRequest.Site.Page != curl { t.Errorf("Expected Site.Page to equal curl (%s), got: %s", curl, resolvedRequest.Site.Page) } + + if resolvedRequest.Site == nil || resolvedRequest.Site.Publisher == nil || resolvedRequest.Site.Publisher.ID != account { + t.Errorf("Expected Site.Publisher.ID to equal (%s), got: %s", account, resolvedRequest.Site.Publisher.ID) + } } func TestOverrideDimensions(t *testing.T) { @@ -876,6 +881,7 @@ type formatOverrideSpec struct { overrideWidth uint64 overrideHeight uint64 multisize string + account string expect []openrtb.Format } @@ -897,7 +903,7 @@ func (s formatOverrideSpec) execute(t *testing.T) { openrtb_ext.BidderMap, ) - url := fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&debug=1&w=%d&h=%d&ow=%d&oh=%d&ms=%s", s.width, s.height, s.overrideWidth, s.overrideHeight, s.multisize) + url := fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&debug=1&w=%d&h=%d&ow=%d&oh=%d&ms=%s&account=%s", s.width, s.height, s.overrideWidth, s.overrideHeight, s.multisize, s.account) request := httptest.NewRequest("GET", url, nil) recorder := httptest.NewRecorder() endpoint(recorder, request, nil) From bfcfefe2d225ad7b09a80567f3a19e4be5f4305a Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Fri, 17 Jul 2020 13:05:19 -0400 Subject: [PATCH 146/318] Facebook Only Supports App Impressions (#1396) --- .../exemplary/banner-site.json | 132 ------------------ .../exemplary/interstitial.json | 12 +- .../exemplary/native-1.1.json | 12 +- .../audienceNetworktest/exemplary/video.json | 12 +- .../supplemental/banner-format-only.json | 12 +- .../supplemental/invalid-adm.json | 12 +- .../supplemental/invalid-banner-height.json | 6 +- .../supplemental/invalid-interstitial.json | 6 +- .../supplemental/missing-adm-bidid.json | 12 +- .../supplemental/missing-adm.json | 12 +- .../supplemental/missing-banner-height.json | 6 +- .../supplemental/multi-imp.json | 18 +-- .../supplemental/no-bid-204.json | 12 +- .../supplemental/no-imps.json | 6 +- .../supplemental/required-buyeruid.json | 6 +- .../required-param-placementId.json | 6 +- .../required-param-publisherId.json | 6 +- .../supplemental/server-error-500.json | 12 +- .../supplemental/site-not-supported.json | 38 +++++ .../supplemental/split-placementId.json | 12 +- adapters/audienceNetwork/facebook.go | 20 ++- adapters/audienceNetwork/facebook_test.go | 17 --- static/bidder-info/audienceNetwork.yaml | 5 - 23 files changed, 137 insertions(+), 255 deletions(-) delete mode 100644 adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json create mode 100644 adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json deleted file mode 100644 index 01bab3dfd71..00000000000 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "mockBidRequest": { - "id": "test-req-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - } - ], - "w": 300, - "h": 250 - }, - "ext": { - "bidder": { - "publisherid": "123", - "placementid": "456" - } - } - } - ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" - }, - "device": { - "ip": "152.193.6.74" - }, - "user": { - "id": "db089de9-a62e-4861-a881-0ff15e052516", - "buyeruid": "v4_bidder_token" - }, - "tmax": 500 - }, - "httpcalls": [ - { - "expectedRequest": { - "uri": "https://an.facebook.com/placementbid.ortb", - "headers": { - "Accept": [ - "application/json" - ], - "Content-Type": [ - "application/json;charset=utf-8" - ], - "X-Fb-Pool-Routing-Token": [ - "v4_bidder_token" - ] - }, - "body": { - "id": "test-imp-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "w": -1, - "h": 250 - }, - "tagid": "123_456" - } - ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", - "publisher": { - "id": "123" - } - }, - "device": { - "ip": "152.193.6.74" - }, - "user": { - "id": "db089de9-a62e-4861-a881-0ff15e052516", - "buyeruid": "v4_bidder_token" - }, - "tmax": 500, - "ext": { - "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174", - "platformid": "test-platform-id" - } - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "test-imp-id", - "seatbid": [ - { - "bid": [ - { - "id": "987", - "impid": "test-imp-id", - "price": 1.000000, - "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}", - "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", - "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", - "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" - } - ] - } - ], - "bidid": "654", - "cur": "USD" - } - } - } - ], - "expectedBidResponses": [ - { - "currency": "USD", - "bids": [ - { - "bid": { - "id": "987", - "impid": "test-imp-id", - "price": 1, - "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}", - "adid": "987", - "crid": "987", - "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", - "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0", - "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}" - }, - "type": "banner" - } - ] - } - ] -} diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json index 9f563f11948..573032c81e1 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json @@ -23,9 +23,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -64,9 +64,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json index 16bed344767..08639bee013 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json @@ -16,9 +16,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -56,9 +56,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json index 5ece0f08530..35bdf9a443e 100644 --- a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json +++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json @@ -21,9 +21,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -66,9 +66,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json index 5469fefbd65..450e0d9e45b 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json @@ -24,9 +24,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -64,9 +64,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json index f145f5fe4ce..c33807bda74 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json @@ -18,9 +18,9 @@ } } }], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -55,9 +55,9 @@ }, "tagid": "123_456" }], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json index fa9fd9132b8..b229d41a27a 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json @@ -22,9 +22,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json index ad19d94c6e9..68ca8044812 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json @@ -20,9 +20,9 @@ } } }], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json index b57c900104e..50212155752 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json @@ -18,9 +18,9 @@ } } }], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -55,9 +55,9 @@ }, "tagid": "123_456" }], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json index 23227aab959..832b16dca22 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json @@ -18,9 +18,9 @@ } } }], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -55,9 +55,9 @@ }, "tagid": "123_456" }], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json index 016e8de0ef0..0793f990049 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json @@ -20,9 +20,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json index 231c2826548..682c33e46b8 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json @@ -41,9 +41,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -81,9 +81,9 @@ "tagid": "pub1_plmt1" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "pub1" } @@ -152,9 +152,9 @@ "tagid": "pub2_plmt2" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "pub2" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json index 45b35e05dd9..642e495810a 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json @@ -16,9 +16,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -56,9 +56,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json index 7420f7e8fb2..fccdf71ca4a 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json @@ -2,9 +2,9 @@ "mockBidRequest": { "id": "test-req-id", "imp": [], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json index 964dcb48b48..72b4fbacdd1 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json @@ -26,9 +26,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json index a9c3c23d298..f13b70e1be2 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json @@ -25,9 +25,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json index c50f3d36378..a80a1e09b65 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json @@ -25,9 +25,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json index 7ff8886139a..f0a11905cf8 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json @@ -14,9 +14,9 @@ } } }], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -51,9 +51,9 @@ }, "tagid": "123_456" }], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json new file mode 100644 index 00000000000..9155352a192 --- /dev/null +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json @@ -0,0 +1,38 @@ +{ + "mockBidRequest": { + "id": "test-req-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherid": "123", + "placementid": "456" + } + } + }], + "site": { + "domain": "prebid.org", + "page": "prebid.org" + }, + "device": { + "ip": "152.193.6.74" + }, + "user": { + "id": "db089de9-a62e-4861-a881-0ff15e052516", + "buyeruid": "v4_bidder_token" + }, + "tmax": 500 + }, + "expectedMakeRequestsErrors": [{ + "value": "Site impressions are not supported.", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json index 34c1eccc58e..45c34192ea2 100644 --- a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json +++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json @@ -21,9 +21,9 @@ } } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org" + "app": { + "id": "app-abc", + "bundle": "com.prebid" }, "device": { "ip": "152.193.6.74" @@ -50,9 +50,9 @@ "tagid": "123_456" } ], - "site": { - "domain": "prebid.org", - "page": "prebid.org", + "app": { + "id": "app-abc", + "bundle": "com.prebid", "publisher": { "id": "123" } diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index f4091e4e23c..d9f6719fd17 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -54,6 +54,12 @@ func (this *FacebookAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo * }} } + if request.Site != nil { + return nil, []error{&errortypes.BadInput{ + Message: "Site impressions are not supported.", + }} + } + return this.buildRequests(request) } @@ -143,10 +149,6 @@ func (this *FacebookAdapter) modifyRequest(out *openrtb.BidRequest) error { app := *out.App app.Publisher = &openrtb.Publisher{ID: pubId} out.App = &app - } else { - site := *out.Site - site.Publisher = &openrtb.Publisher{ID: pubId} - out.Site = &site } if err = this.modifyImp(imp); err != nil { @@ -468,15 +470,11 @@ func (fa *FacebookAdapter) MakeTimeoutNotification(req *adapters.RequestData) (* return &adapters.RequestData{}, []error{err} } - // The publisher ID is either in the app object or the site object, depending on the supply of the request so we need - // to check both + // The publisher ID is expected in the app object pubID, err = jsonparser.GetString(req.Body, "app", "publisher", "id") if err != nil { - pubID, err = jsonparser.GetString(req.Body, "site", "publisher", "id") - if err != nil { - return &adapters.RequestData{}, []error{ - errors.New("path [app|site].publisher.id not found in the request"), - } + return &adapters.RequestData{}, []error{ + errors.New("path app.publisher.id not found in the request"), } } diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go index 7f567d6137b..912f12223f8 100644 --- a/adapters/audienceNetwork/facebook_test.go +++ b/adapters/audienceNetwork/facebook_test.go @@ -61,23 +61,6 @@ func TestMakeTimeoutNoticeApp(t *testing.T) { assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.") } -func TestMakeTimeoutNoticeSite(t *testing.T) { - req := adapters.RequestData{ - Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"site":{"publisher":{"id":"5678"}}}`), - } - fba := NewFacebookBidder("test-platform-id", "test-app-secret") - - tb, ok := fba.(adapters.TimeoutBidder) - if !ok { - t.Error("Facebook adapter is not a TimeoutAdapter") - } - - toReq, err := tb.MakeTimeoutNotification(&req) - assert.Nil(t, err, "Facebook MakeTimeoutNotification() return an error %v", err) - expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=5678&auction=1234&ortb_loss_code=2" - assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.") -} - func TestMakeTimeoutNoticeBadRequest(t *testing.T) { req := adapters.RequestData{ Body: []byte(`{"imp":[{{"id":"1234"}}`), diff --git a/static/bidder-info/audienceNetwork.yaml b/static/bidder-info/audienceNetwork.yaml index 56230bf3f9a..324e5c6dff8 100644 --- a/static/bidder-info/audienceNetwork.yaml +++ b/static/bidder-info/audienceNetwork.yaml @@ -1,11 +1,6 @@ maintainer: email: "none" capabilities: - site: - mediaTypes: - - banner - - video - - native app: mediaTypes: - banner From c889570b14dac808f95fd46d7254d124e7b0c226 Mon Sep 17 00:00:00 2001 From: Ad Generation Date: Sat, 18 Jul 2020 02:39:31 +0900 Subject: [PATCH 147/318] fix: Change currency of ad-generation's bidResponse according to bidRequest (#1383) --- adapters/adgeneration/adgeneration.go | 3 +- adapters/adgeneration/adgeneration_test.go | 63 +++++++++++++++++++ .../exemplary/single-banner.json | 2 +- .../supplemental/204-bid-response.json | 2 +- .../supplemental/400-bid-response.json | 2 +- .../supplemental/no-bid-response.json | 2 +- 6 files changed, 69 insertions(+), 5 deletions(-) diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go index 4b1215dea9d..054fa7f6df3 100644 --- a/adapters/adgeneration/adgeneration.go +++ b/adapters/adgeneration/adgeneration.go @@ -210,6 +210,7 @@ func (adg *AdgenerationAdapter) MakeBids(internalRequest *openrtb.BidRequest, ex Bid: &bid, BidType: bitType, }) + bidResponse.Currency = adg.getCurrency(internalRequest) return bidResponse, nil } } @@ -254,7 +255,7 @@ func removeWrapper(ad string) string { func NewAdgenerationAdapter(endpoint string) *AdgenerationAdapter { return &AdgenerationAdapter{ endpoint, - "1.0.0", + "1.0.1", "JPY", } } diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go index e76995fc5e4..3c795ea28a8 100644 --- a/adapters/adgeneration/adgeneration_test.go +++ b/adapters/adgeneration/adgeneration_test.go @@ -5,7 +5,9 @@ import ( "testing" "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/stretchr/testify/assert" ) func TestJsonSamples(t *testing.T) { @@ -174,3 +176,64 @@ func TestCreateAd(t *testing.T) { t.Errorf("%v does not match createAd.", adgVastResponse) } } + +func TestMakeBids(t *testing.T) { + bidder := NewAdgenerationAdapter("https://d.socdm.com/adsv/v1") + internalRequest := &openrtb.BidRequest{ + ID: "test-success-bid-request", + Imp: []openrtb.Imp{ + {ID: "bidRequest-success-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{"bidder": { "id": "58278" }}`)}, + }, + Device: &openrtb.Device{UA: "testUA", IP: "testIP"}, + Site: &openrtb.Site{Page: "https://supership.com"}, + User: &openrtb.User{BuyerUID: "buyerID"}, + } + externalRequest := adapters.RequestData{} + response := adapters.ResponseData{ + StatusCode: 200, + Body: ([]byte)("{\n \"ad\": \"testAd\",\n \"cpm\": 30,\n \"creativeid\": \"Dummy_supership.jp\",\n \"h\": 250,\n \"locationid\": \"58278\",\n \"results\": [{}],\n \"dealid\": \"test-deal-id\",\n \"w\": 300\n }"), + } + // default Currency InternalRequest + defaultCurBidderResponse, errs := bidder.MakeBids(internalRequest, &externalRequest, &response) + if len(errs) > 0 { + t.Errorf("MakeBids return errors. errors: %v", errs) + } + checkBidResponse(t, defaultCurBidderResponse, bidder.defaultCurrency) + + // Specified Currency InternalRequest + usdCur := "USD" + internalRequest.Cur = []string{usdCur} + specifiedCurBidderResponse, errs := bidder.MakeBids(internalRequest, &externalRequest, &response) + if len(errs) > 0 { + t.Errorf("MakeBids return errors. errors: %v", errs) + } + checkBidResponse(t, specifiedCurBidderResponse, usdCur) + +} + +func checkBidResponse(t *testing.T, bidderResponse *adapters.BidderResponse, expectedCurrency string) { + if bidderResponse == nil { + t.Errorf("actual bidResponse is nil.") + } + + // AdM is assured by TestCreateAd and JSON tests + var expectedAdM string = "testAd" + var expectedID string = "58278" + var expectedImpID = "bidRequest-success-test" + var expectedPrice float64 = 30.0 + var expectedW uint64 = 300 + var expectedH uint64 = 250 + var expectedCrID string = "Dummy_supership.jp" + var extectedDealID string = "test-deal-id" + + assert.Equal(t, expectedCurrency, bidderResponse.Currency) + assert.Equal(t, 1, len(bidderResponse.Bids)) + assert.Equal(t, expectedID, bidderResponse.Bids[0].Bid.ID) + assert.Equal(t, expectedImpID, bidderResponse.Bids[0].Bid.ImpID) + assert.Equal(t, expectedAdM, bidderResponse.Bids[0].Bid.AdM) + assert.Equal(t, expectedPrice, bidderResponse.Bids[0].Bid.Price) + assert.Equal(t, expectedW, bidderResponse.Bids[0].Bid.W) + assert.Equal(t, expectedH, bidderResponse.Bids[0].Bid.H) + assert.Equal(t, expectedCrID, bidderResponse.Bids[0].Bid.CrID) + assert.Equal(t, extectedDealID, bidderResponse.Bids[0].Bid.DealID) +} diff --git a/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json index d23a510bee5..10bf1c4a0c0 100644 --- a/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json +++ b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json @@ -52,7 +52,7 @@ "tmax": 500 }, "expectedRequest":{ - "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", + "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", "headers": { "Accept": [ "application/json" diff --git a/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json index cf8635bbc3d..bc469a1e3a9 100644 --- a/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json +++ b/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json @@ -52,7 +52,7 @@ "tmax": 500 }, "expectedRequest":{ - "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", + "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", "headers": { "Accept": [ "application/json" diff --git a/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json index f5dc7fe0af5..6ac92d9a38b 100644 --- a/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json +++ b/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json @@ -52,7 +52,7 @@ "tmax": 500 }, "expectedRequest":{ - "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", + "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", "headers": { "Accept": [ "application/json" diff --git a/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json index 399f85a5856..a0abb66d039 100644 --- a/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json +++ b/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json @@ -52,7 +52,7 @@ "tmax": 500 }, "expectedRequest":{ - "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", + "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1¤cy=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html", "headers": { "Accept": [ "application/json" From 6b7c113b76ed202c9022d063bc5b713ae53ae0a6 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Fri, 17 Jul 2020 22:50:22 -0400 Subject: [PATCH 148/318] Adding primary categories to freewheel mapping (#1407) --- .../category-mapping/freewheel/freewheel.json | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/static/category-mapping/freewheel/freewheel.json b/static/category-mapping/freewheel/freewheel.json index 1c4a4fa2471..11529206087 100644 --- a/static/category-mapping/freewheel/freewheel.json +++ b/static/category-mapping/freewheel/freewheel.json @@ -1178,5 +1178,81 @@ "IAB22-3": { "id": "410", "name": "Product" + }, + "IAB1": { + "id": "392", + "name": "Entertainment" + }, + "IAB2": { + "id": "399", + "name": "Automotive" + }, + "IAB3": { + "id": "393", + "name": "Business Services" + }, + "IAB4": { + "id": "405", + "name": "Educational Services" + }, + "IAB5": { + "id": "405", + "name": "Educational Services" + }, + "IAB7": { + "id": "406", + "name": "Health Care Services" + }, + "IAB8": { + "id": "394", + "name": "Food" + }, + "IAB9": { + "id": "392", + "name": "Entertainment" + }, + "IAB10": { + "id": "434", + "name": "Home Furnishings" + }, + "IAB11": { + "id": "398", + "name": "Government/Municipal" + }, + "IAB12": { + "id": "438", + "name": "News" + }, + "IAB13": { + "id": "393", + "name": "Business Services" + }, + "IAB16": { + "id": "423", + "name": "Pet Food/Supplies" + }, + "IAB17": { + "id": "425", + "name": "Professional Sports" + }, + "IAB18": { + "id": "397", + "name": "Apparel" + }, + "IAB19": { + "id": "409", + "name": "Computing Product" + }, + "IAB20": { + "id": "395", + "name": "Travel/Hotel/Airlines" + }, + "IAB21": { + "id": "416", + "name": "Real Estate" + }, + "IAB22": { + "id": "403", + "name": "Retail Stores/Chains" } } \ No newline at end of file From a5962de9a5900f3b205dfac1263f53d7daf96eec Mon Sep 17 00:00:00 2001 From: guscarreon Date: Wed, 22 Jul 2020 13:11:25 -0400 Subject: [PATCH 149/318] Add Outgoing Connection Metrics (#1343) --- config/config.go | 6 + config/config_test.go | 3 + exchange/adapter_map.go | 2 +- exchange/bidder.go | 77 ++++++++-- exchange/bidder_test.go | 130 ++++++++++++++--- exchange/targeting_test.go | 2 +- go.mod | 1 + go.sum | 5 + pbsmetrics/config/metrics.go | 25 +++- pbsmetrics/go_metrics.go | 52 ++++++- pbsmetrics/go_metrics_test.go | 139 ++++++++++++++++++ pbsmetrics/metrics.go | 2 + pbsmetrics/metrics_mock.go | 10 ++ pbsmetrics/prometheus/preload.go | 14 ++ pbsmetrics/prometheus/prometheus.go | 105 +++++++++++--- pbsmetrics/prometheus/prometheus_test.go | 174 ++++++++++++++++++++++- 16 files changed, 683 insertions(+), 64 deletions(-) diff --git a/config/config.go b/config/config.go index 2e7f875b023..a82dbb5edf7 100755 --- a/config/config.go +++ b/config/config.go @@ -379,6 +379,11 @@ type Metrics struct { type DisabledMetrics struct { // True if we want to stop collecting account-to-adapter metrics AccountAdapterDetails bool `mapstructure:"account_adapter_details"` + + // True if we don't want to collect metrics about the connections prebid + // server establishes with bidder servers such as the number of connections + // that were created or reused. + AdapterConnectionMetrics bool `mapstructure:"adapter_connections_metrics"` } func (cfg *Metrics) validate(errs configErrors) configErrors { @@ -688,6 +693,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("http_client_cache.idle_connection_timeout_seconds", 60) // no metrics configured by default (metrics{host|database|username|password}) v.SetDefault("metrics.disabled_metrics.account_adapter_details", false) + v.SetDefault("metrics.disabled_metrics.adapter_connections_metrics", true) v.SetDefault("metrics.influxdb.host", "") v.SetDefault("metrics.influxdb.database", "") v.SetDefault("metrics.influxdb.username", "") diff --git a/config/config_test.go b/config/config_test.go index 3456694db5c..4774d9d6e46 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -33,6 +33,7 @@ func TestDefaults(t *testing.T) { cmpBools(t, "account_required", cfg.AccountRequired, false) cmpInts(t, "metrics.influxdb.collection_rate_seconds", cfg.Metrics.Influxdb.MetricSendInterval, 20) cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, false) + cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true) cmpStrings(t, "certificates_file", cfg.PemCertsFile, "") } @@ -89,6 +90,7 @@ metrics: metric_send_interval: 30 disabled_metrics: account_adapter_details: true + adapter_connections_metrics: true datacache: type: postgres filename: /usr/db/db.db @@ -294,6 +296,7 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "adapters.rhythmone.usersync_url", cfg.Adapters[string(openrtb_ext.BidderRhythmone)].UserSyncURL, "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Fprebid-server.prebid.org%2F%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BRX_UUID%5D") cmpBools(t, "account_required", cfg.AccountRequired, true) cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true) + cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true) cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem") cmpStrings(t, "request_validation.ipv4_private_networks", cfg.RequestValidation.IPv4PrivateNetworks[0], "1.1.1.0/24") cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[0], "1111::/16") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 53607ac57d8..2ecddb83cfc 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -201,7 +201,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter for name, bidder := range ortbBidders { // Clean out any disabled bidders if infos[string(name)].Status == adapters.StatusActive { - allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client, cfg, me) + allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client, cfg, me, name) } } diff --git a/exchange/bidder.go b/exchange/bidder.go index ee6a4942147..7c39b72b348 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "net/http" + "net/http/httptrace" "time" "github.com/golang/glog" @@ -87,20 +88,30 @@ type pbsOrtbSeatBid struct { // // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter" // (which is being phased out and replaced by Bidder for OpenRTB auctions) -func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me pbsmetrics.MetricsEngine) adaptedBidder { +func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me pbsmetrics.MetricsEngine, name openrtb_ext.BidderName) adaptedBidder { return &bidderAdapter{ - Bidder: bidder, - Client: client, - DebugConfig: cfg.Debug, - me: me, + Bidder: bidder, + BidderName: name, + Client: client, + me: me, + config: bidderAdapterConfig{ + Debug: cfg.Debug, + DisableConnMetrics: cfg.Metrics.Disabled.AdapterConnectionMetrics, + }, } } type bidderAdapter struct { - Bidder adapters.Bidder - Client *http.Client - DebugConfig config.Debug - me pbsmetrics.MetricsEngine + Bidder adapters.Bidder + BidderName openrtb_ext.BidderName + Client *http.Client + me pbsmetrics.MetricsEngine + config bidderAdapterConfig +} + +type bidderAdapterConfig struct { + Debug config.Debug + DisableConnMetrics bool } func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currencies.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { @@ -325,6 +336,11 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re } httpReq.Header = req.Headers + // If adapter connection metrics are not disabled, add the client trace + // to get complete connection info into our metrics + if !bidder.config.DisableConnMetrics { + ctx = bidder.addClientTrace(ctx) + } httpResp, err := ctxhttp.Do(ctx, bidder.Client, httpReq) if err != nil { if err == context.DeadlineExceeded { @@ -387,7 +403,7 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou httpResp, err := ctxhttp.Do(ctx, bidder.Client, httpReq) success := (err == nil && httpResp.StatusCode >= 200 && httpResp.StatusCode < 300) bidder.me.RecordTimeoutNotice(success) - if bidder.DebugConfig.TimeoutNotification.Log && !(bidder.DebugConfig.TimeoutNotification.FailOnly && success) { + if bidder.config.Debug.TimeoutNotification.Log && !(bidder.config.Debug.TimeoutNotification.FailOnly && success) { var msg string if err == nil { msg = fmt.Sprintf("TimeoutNotification: status:(%d) body:%s", httpResp.StatusCode, string(toReq.Body)) @@ -395,16 +411,16 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou msg = fmt.Sprintf("TimeoutNotification: error:(%s) body:%s", err.Error(), string(toReq.Body)) } // If logging is turned on, and logging is not disallowed via FailOnly - util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate) } } else { bidder.me.RecordTimeoutNotice(false) - if bidder.DebugConfig.TimeoutNotification.Log { + if bidder.config.Debug.TimeoutNotification.Log { msg := fmt.Sprintf("TimeoutNotification: Failed to make timeout request: method(%s), uri(%s), error(%s)", toReq.Method, toReq.Uri, err.Error()) - util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate) } } - } else if bidder.DebugConfig.TimeoutNotification.Log { + } else if bidder.config.Debug.TimeoutNotification.Log { reqJSON, err := json.Marshal(req) var msg string if err == nil { @@ -412,7 +428,7 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou } else { msg = fmt.Sprintf("TimeoutNotification: Failed to generate timeout request: error(%s), bidder request marshal failed(%s)", errL[0].Error(), err.Error()) } - util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate) + util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate) } } @@ -422,3 +438,34 @@ type httpCallInfo struct { response *adapters.ResponseData err error } + +// This function adds an httptrace.ClientTrace object to the context so, if connection with the bidder +// endpoint is established, we can keep track of whether the connection was newly created, reused, and +// the time from the connection request, to the connection creation. +func (bidder *bidderAdapter) addClientTrace(ctx context.Context) context.Context { + var connStart, dnsStart time.Time + + trace := &httptrace.ClientTrace{ + // GetConn is called before a connection is created or retrieved from an idle pool + GetConn: func(hostPort string) { + connStart = time.Now() + }, + // GotConn is called after a successful connection is obtained + GotConn: func(info httptrace.GotConnInfo) { + connWaitTime := time.Now().Sub(connStart) + + bidder.me.RecordAdapterConnections(bidder.BidderName, info.Reused, connWaitTime) + }, + // DNSStart is called when a DNS lookup begins. + DNSStart: func(info httptrace.DNSStartInfo) { + dnsStart = time.Now() + }, + // DNSDone is called when a DNS lookup ends. + DNSDone: func(info httptrace.DNSDoneInfo) { + dnsLookupTime := time.Now().Sub(dnsStart) + + bidder.me.RecordDNSTime(dnsLookupTime) + }, + } + return httptrace.WithClientTrace(ctx, trace) +} diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index d4fc0cf7cd3..1a27b72aa12 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -6,8 +6,11 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "net/http" "net/http/httptest" + "net/http/httptrace" + "strings" "testing" "time" @@ -17,8 +20,11 @@ import ( "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/pbsmetrics" + metricsConf "github.com/prebid/prebid-server/pbsmetrics/config" metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" nativeRequests "github.com/mxmCherry/openrtb/native/request" nativeResponse "github.com/mxmCherry/openrtb/native/response" @@ -68,7 +74,7 @@ func TestSingleBidder(t *testing.T) { }, bidResponse: mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) @@ -156,7 +162,7 @@ func TestMultiBidder(t *testing.T) { }}, bidResponse: mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) @@ -194,8 +200,10 @@ func TestBidderTimeout(t *testing.T) { defer server.Close() bidder := &bidderAdapter{ - Bidder: &mixedMultiBidder{}, - Client: server.Client(), + Bidder: &mixedMultiBidder{}, + BidderName: openrtb_ext.BidderAppnexus, + Client: server.Client(), + me: &metricsConf.DummyMetricsEngine{}, } callInfo := bidder.doRequest(ctx, &adapters.RequestData{ @@ -235,8 +243,10 @@ func TestConnectionClose(t *testing.T) { server = httptest.NewServer(handler) bidder := &bidderAdapter{ - Bidder: &mixedMultiBidder{}, - Client: server.Client(), + Bidder: &mixedMultiBidder{}, + Client: server.Client(), + BidderName: openrtb_ext.BidderAppnexus, + me: &metricsConf.DummyMetricsEngine{}, } callInfo := bidder.doRequest(context.Background(), &adapters.RequestData{ @@ -514,7 +524,7 @@ func TestMultiCurrencies(t *testing.T) { ) // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -663,7 +673,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() seatBid, errs := bidder.requestBid( context.Background(), @@ -829,7 +839,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -945,7 +955,7 @@ func TestServerCallDebugging(t *testing.T) { Headers: http.Header{}, }, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() bids, _ := bidder.requestBid( @@ -1057,7 +1067,7 @@ func TestMobileNativeTypes(t *testing.T) { }, bidResponse: tc.mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() seatBids, _ := bidder.requestBid( @@ -1078,7 +1088,7 @@ func TestMobileNativeTypes(t *testing.T) { } func TestErrorReporting(t *testing.T) { - bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) currencyConverter := currencies.NewRateConverterDefault() bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if bids != nil { @@ -1233,6 +1243,82 @@ func TestSetAssetTypes(t *testing.T) { } } +func TestCallRecordAdapterConnections(t *testing.T) { + // Setup mock server + respStatus := 200 + respBody := "{\"bid\":false}" + server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) + defer server.Close() + + // declare requestBid parameters + bidAdjustment := 2.0 + + bidderImpl := &goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + Body: []byte("{\"key\":\"val\"}"), + Headers: http.Header{}, + }, + bidResponse: &adapters.BidderResponse{}, + } + + // setup a mock metrics engine and its expectation + metrics := &pbsmetrics.MetricsEngineMock{} + expectedAdapterName := openrtb_ext.BidderAppnexus + compareConnWaitTime := func(dur time.Duration) bool { return dur.Nanoseconds() > 0 } + + metrics.On("RecordAdapterConnections", expectedAdapterName, false, mock.MatchedBy(compareConnWaitTime)).Once() + + // Run requestBid using an http.Client with a mock handler + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus) + _, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencies.NewRateConverterDefault().Rates(), &adapters.ExtraRequestInfo{}) + + // Assert no errors + assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs) + + // Assert RecordAdapterConnections() was called with the parameters we expected + metrics.AssertExpectations(t) +} + +type DNSDoneTripper struct{} + +func (DNSDoneTripper) RoundTrip(req *http.Request) (*http.Response, error) { + //Access the httptrace.ClientTrace + trace := httptrace.ContextClientTrace(req.Context()) + + //Force DNSDone call defined in exchange/bidder.go + trace.DNSDone(httptrace.DNSDoneInfo{}) + + resp := &http.Response{ + StatusCode: 200, + Header: map[string][]string{"Location": {"http://www.example.com/"}}, + Body: ioutil.NopCloser(strings.NewReader("postBody")), + } + return resp, nil +} + +func TestCallRecordRecordDNSTime(t *testing.T) { + // setup a mock metrics engine and its expectation + metricsMock := &pbsmetrics.MetricsEngineMock{} + metricsMock.Mock.On("RecordDNSTime", mock.Anything).Return() + + // Instantiate the bidder that will send the request. We'll make sure to use an + // http.Client that runs our mock RoundTripper so DNSDone(httptrace.DNSDoneInfo{}) + // gets called + bidder := &bidderAdapter{ + Bidder: &mixedMultiBidder{}, + Client: &http.Client{Transport: DNSDoneTripper{}}, + me: metricsMock, + } + + // Run test + bidder.doRequest(context.Background(), &adapters.RequestData{Method: "POST", Uri: "http://www.example.com/"}) + + // Tried one or another, none seem to work without panicking + metricsMock.AssertExpectations(t) +} + func TestTimeoutNotificationOff(t *testing.T) { respBody := "{\"bid\":false}" respStatus := 200 @@ -1248,10 +1334,10 @@ func TestTimeoutNotificationOff(t *testing.T) { }, } bidder := &bidderAdapter{ - Bidder: bidderImpl, - Client: server.Client(), - DebugConfig: config.Debug{}, - me: &metricsConfig.DummyMetricsEngine{}, + Bidder: bidderImpl, + Client: server.Client(), + config: bidderAdapterConfig{Debug: config.Debug{}}, + me: &metricsConf.DummyMetricsEngine{}, } if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok { t.Error("Failed to cast bidder to a TimeoutBidder") @@ -1284,13 +1370,15 @@ func TestTimeoutNotificationOn(t *testing.T) { bidderAdapter := &bidderAdapter{ Bidder: bidderWrappedWithInfo, Client: server.Client(), - DebugConfig: config.Debug{ - TimeoutNotification: config.TimeoutNotification{ - Log: true, - SamplingRate: 1.0, + config: bidderAdapterConfig{ + Debug: config.Debug{ + TimeoutNotification: config.TimeoutNotification{ + Log: true, + SamplingRate: 1.0, + }, }, }, - me: &metricsConfig.DummyMetricsEngine{}, + me: &metricsConf.DummyMetricsEngine{}, } // Unwrap To Mimic exchange.go Casting Code diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 72de1d4261f..16955e97c5b 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -134,7 +134,7 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb.Bid, mockServerU adapterMap[bidder] = adaptBidder(&mockTargetingBidder{ mockServerURL: mockServerURL, bids: bids, - }, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}) + }, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) } return adapterMap } diff --git a/go.mod b/go.mod index 72bb9b74886..00cadd31ce1 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/influxdata/influxdb v1.6.1 // indirect github.com/julienschmidt/httprouter v1.1.0 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect + github.com/kr/pretty v0.2.0 // indirect github.com/lib/pq v1.0.0 github.com/magiconair/properties v1.8.0 github.com/mattn/go-colorable v0.1.2 // indirect diff --git a/go.sum b/go.sum index 35b2b76591d..5eaf37cad9f 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,11 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go index 0dbe9a69d9f..6a36f9e71c0 100644 --- a/pbsmetrics/config/metrics.go +++ b/pbsmetrics/config/metrics.go @@ -37,7 +37,7 @@ func NewMetricsEngine(cfg *config.Configuration, adapterList []openrtb_ext.Bidde } if cfg.Metrics.Prometheus.Port != 0 { // Set up the Prometheus metrics. - returnEngine.PrometheusMetrics = prometheusmetrics.NewMetrics(cfg.Metrics.Prometheus) + returnEngine.PrometheusMetrics = prometheusmetrics.NewMetrics(cfg.Metrics.Prometheus, cfg.Metrics.Disabled) engineList = append(engineList, returnEngine.PrometheusMetrics) } @@ -118,6 +118,21 @@ func (me *MultiMetricsEngine) RecordAdapterRequest(labels pbsmetrics.AdapterLabe } } +// Keeps track of created and reused connections to adapter bidders and the time from the +// connection request, to the connection creation, or reuse from the pool across all engines +func (me *MultiMetricsEngine) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { + for _, thisME := range *me { + thisME.RecordAdapterConnections(bidderName, connWasReused, connWaitTime) + } +} + +// Times the DNS resolution process +func (me *MultiMetricsEngine) RecordDNSTime(dnsLookupTime time.Duration) { + for _, thisME := range *me { + thisME.RecordDNSTime(dnsLookupTime) + } +} + // RecordAdapterBidReceived across all engines func (me *MultiMetricsEngine) RecordAdapterBidReceived(labels pbsmetrics.AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) { for _, thisME := range *me { @@ -237,6 +252,14 @@ func (me *DummyMetricsEngine) RecordAdapterPanic(labels pbsmetrics.AdapterLabels func (me *DummyMetricsEngine) RecordAdapterRequest(labels pbsmetrics.AdapterLabels) { } +// RecordAdapterConnections as a noop +func (me *DummyMetricsEngine) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { +} + +// RecordDNSTime as a noop +func (me *DummyMetricsEngine) RecordDNSTime(dnsLookupTime time.Duration) { +} + // RecordAdapterBidReceived as a noop func (me *DummyMetricsEngine) RecordAdapterBidReceived(labels pbsmetrics.AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) { } diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go index 836434bf25e..26f6ce07b29 100644 --- a/pbsmetrics/go_metrics.go +++ b/pbsmetrics/go_metrics.go @@ -29,6 +29,7 @@ type Metrics struct { PrebidCacheRequestTimerError metrics.Timer StoredReqCacheMeter map[CacheResult]metrics.Meter StoredImpCacheMeter map[CacheResult]metrics.Meter + DNSLookupTimer metrics.Timer // Metrics for OpenRTB requests specifically. So we can track what % of RequestsMeter are OpenRTB // and know when legacy requests have been abandoned. @@ -81,6 +82,9 @@ type AdapterMetrics struct { BidsReceivedMeter metrics.Meter PanicMeter metrics.Meter MarkupMetrics map[openrtb_ext.BidType]*MarkupDeliveryMetrics + ConnCreated metrics.Counter + ConnReused metrics.Counter + ConnWaitTime metrics.Timer } type MarkupDeliveryMetrics struct { @@ -106,7 +110,7 @@ const unknownBidder openrtb_ext.BidderName = "unknown" // rather than loading legacy metrics that never get filled. // This will also eventually let us configure metrics, such as setting a limited set of metrics // for a production instance, and then expanding again when we need more debugging. -func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disableMetrics config.DisabledMetrics) *Metrics { +func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disabledMetrics config.DisabledMetrics) *Metrics { blankMeter := &metrics.NilMeter{} blankTimer := &metrics.NilTimer{} @@ -123,6 +127,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa SafariRequestMeter: blankMeter, SafariNoCookieMeter: blankMeter, RequestTimer: blankTimer, + DNSLookupTimer: blankTimer, RequestsQueueTimer: make(map[RequestType]map[bool]metrics.Timer), PrebidCacheRequestTimerSuccess: blankTimer, PrebidCacheRequestTimerError: blankTimer, @@ -153,13 +158,13 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa AdapterMetrics: make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)), accountMetrics: make(map[string]*accountMetrics), - MetricsDisabled: disableMetrics, + MetricsDisabled: disabledMetrics, exchanges: exchanges, } for _, a := range exchanges { - newMetrics.AdapterMetrics[a] = makeBlankAdapterMetrics() + newMetrics.AdapterMetrics[a] = makeBlankAdapterMetrics(newMetrics.MetricsDisabled) } for _, t := range RequestTypes() { @@ -209,6 +214,7 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d newMetrics.AppRequestMeter = metrics.GetOrRegisterMeter("app_requests", registry) newMetrics.SafariNoCookieMeter = metrics.GetOrRegisterMeter("safari_no_cookie_requests", registry) newMetrics.RequestTimer = metrics.GetOrRegisterTimer("request_time", registry) + newMetrics.DNSLookupTimer = metrics.GetOrRegisterTimer("dns_lookup_time", registry) newMetrics.PrebidCacheRequestTimerSuccess = metrics.GetOrRegisterTimer("prebid_cache_request_time.ok", registry) newMetrics.PrebidCacheRequestTimerError = metrics.GetOrRegisterTimer("prebid_cache_request_time.err", registry) @@ -255,7 +261,7 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d } // Part of setting up blank metrics, the adapter metrics. -func makeBlankAdapterMetrics() *AdapterMetrics { +func makeBlankAdapterMetrics(disabledMetrics config.DisabledMetrics) *AdapterMetrics { blankMeter := &metrics.NilMeter{} newAdapter := &AdapterMetrics{ NoCookieMeter: blankMeter, @@ -268,6 +274,11 @@ func makeBlankAdapterMetrics() *AdapterMetrics { PanicMeter: blankMeter, MarkupMetrics: makeBlankBidMarkupMetrics(), } + if !disabledMetrics.AdapterConnectionMetrics { + newAdapter.ConnCreated = metrics.NilCounter{} + newAdapter.ConnReused = metrics.NilCounter{} + newAdapter.ConnWaitTime = &metrics.NilTimer{} + } for _, err := range AdapterErrors() { newAdapter.ErrorMeters[err] = blankMeter } @@ -302,6 +313,9 @@ func registerAdapterMetrics(registry metrics.Registry, adapterOrAccount string, openrtb_ext.BidTypeAudio: makeDeliveryMetrics(registry, adapterOrAccount+"."+exchange, openrtb_ext.BidTypeAudio), openrtb_ext.BidTypeNative: makeDeliveryMetrics(registry, adapterOrAccount+"."+exchange, openrtb_ext.BidTypeNative), } + am.ConnCreated = metrics.GetOrRegisterCounter(fmt.Sprintf("%[1]s.%[2]s.connections_created", adapterOrAccount, exchange), registry) + am.ConnReused = metrics.GetOrRegisterCounter(fmt.Sprintf("%[1]s.%[2]s.connections_reused", adapterOrAccount, exchange), registry) + am.ConnWaitTime = metrics.GetOrRegisterTimer(fmt.Sprintf("%[1]s.%[2]s.connection_wait_time", adapterOrAccount, exchange), registry) for err := range am.ErrorMeters { am.ErrorMeters[err] = metrics.GetOrRegisterMeter(fmt.Sprintf("%s.%s.requests.%s", adapterOrAccount, exchange, err), registry) } @@ -348,7 +362,7 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics { am.adapterMetrics = make(map[openrtb_ext.BidderName]*AdapterMetrics, len(me.exchanges)) if !me.MetricsDisabled.AccountAdapterDetails { for _, a := range me.exchanges { - am.adapterMetrics[a] = makeBlankAdapterMetrics() + am.adapterMetrics[a] = makeBlankAdapterMetrics(me.MetricsDisabled) registerAdapterMetrics(me.MetricsRegistry, fmt.Sprintf("account.%s", id), string(a), am.adapterMetrics[a]) } } @@ -472,6 +486,34 @@ func (me *Metrics) RecordAdapterRequest(labels AdapterLabels) { } } +// Keeps track of created and reused connections to adapter bidders and the time from the +// connection request, to the connection creation, or reuse from the pool across all engines +func (me *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, + connWasReused bool, + connWaitTime time.Duration) { + + if me.MetricsDisabled.AdapterConnectionMetrics { + return + } + + am, ok := me.AdapterMetrics[adapterName] + if !ok { + glog.Errorf("Trying to log adapter connection metrics for %s: adapter not found", string(adapterName)) + return + } + + if connWasReused { + am.ConnReused.Inc(1) + } else { + am.ConnCreated.Inc(1) + } + am.ConnWaitTime.Update(connWaitTime) +} + +func (me *Metrics) RecordDNSTime(dnsLookupTime time.Duration) { + me.DNSLookupTimer.Update(dnsLookupTime) +} + // RecordAdapterBidReceived implements a part of the MetricsEngine interface. // This tracks how many bids from each Bidder use `adm` vs. `nurl. func (me *Metrics) RecordAdapterBidReceived(labels AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) { diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go index 2faa08491e0..f676991649d 100644 --- a/pbsmetrics/go_metrics_test.go +++ b/pbsmetrics/go_metrics_test.go @@ -2,6 +2,7 @@ package pbsmetrics import ( "testing" + "time" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" @@ -115,6 +116,10 @@ func ensureContainsAdapterMetrics(t *testing.T, registry metrics.Registry, name ensureContains(t, registry, name+".request_time", adapterMetrics.RequestTimer) ensureContains(t, registry, name+".prices", adapterMetrics.PriceHistogram) ensureContainsBidTypeMetrics(t, registry, name, adapterMetrics.MarkupMetrics) + + ensureContains(t, registry, name+".connections_created", adapterMetrics.ConnCreated) + ensureContains(t, registry, name+".connections_reused", adapterMetrics.ConnReused) + ensureContains(t, registry, name+".connection_wait_time", adapterMetrics.ConnWaitTime) } func TestRecordBidTypeDisabledConfig(t *testing.T) { @@ -179,6 +184,140 @@ func TestRecordBidTypeDisabledConfig(t *testing.T) { } } +func TestRecordDNSTime(t *testing.T) { + testCases := []struct { + description string + inDnsLookupDuration time.Duration + outExpDuration time.Duration + }{ + { + description: "Five second DNS lookup time", + inDnsLookupDuration: time.Second * 5, + outExpDuration: time.Second * 5, + }, + { + description: "Zero DNS lookup time", + inDnsLookupDuration: time.Duration(0), + outExpDuration: time.Duration(0), + }, + } + for _, test := range testCases { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}) + + m.RecordDNSTime(test.inDnsLookupDuration) + + assert.Equal(t, test.outExpDuration.Nanoseconds(), m.DNSLookupTimer.Sum(), test.description) + } +} + +func TestRecordAdapterConnections(t *testing.T) { + var fakeBidder openrtb_ext.BidderName = "fooAdvertising" + + type testIn struct { + adapterName openrtb_ext.BidderName + connWasReused bool + connWait time.Duration + connMetricsDisabled bool + } + + type testOut struct { + expectedConnReusedCount int64 + expectedConnCreatedCount int64 + expectedConnWaitTime time.Duration + } + + testCases := []struct { + description string + in testIn + out testOut + }{ + { + description: "Successful, new connection created, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: false, + connWait: time.Second * 5, + connMetricsDisabled: false, + }, + out: testOut{ + expectedConnReusedCount: 0, + expectedConnCreatedCount: 1, + expectedConnWaitTime: time.Second * 5, + }, + }, + { + description: "Successful, new connection created, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: false, + connWait: time.Second * 4, + connMetricsDisabled: false, + }, + out: testOut{ + expectedConnCreatedCount: 1, + expectedConnWaitTime: time.Second * 4, + }, + }, + { + description: "Successful, was reused, no connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: true, + connMetricsDisabled: false, + }, + out: testOut{ + expectedConnReusedCount: 1, + expectedConnWaitTime: 0, + }, + }, + { + description: "Successful, was reused, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: true, + connWait: time.Second * 5, + connMetricsDisabled: false, + }, + out: testOut{ + expectedConnReusedCount: 1, + expectedConnWaitTime: time.Second * 5, + }, + }, + { + description: "Fake bidder, nothing gets updated", + in: testIn{ + adapterName: fakeBidder, + connWasReused: false, + connWait: 0, + connMetricsDisabled: false, + }, + out: testOut{}, + }, + { + description: "Adapter connection metrics are disabled, nothing gets updated", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: false, + connWait: time.Second * 5, + connMetricsDisabled: true, + }, + out: testOut{}, + }, + } + + for i, test := range testCases { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AdapterConnectionMetrics: test.in.connMetricsDisabled}) + + m.RecordAdapterConnections(test.in.adapterName, test.in.connWasReused, test.in.connWait) + + assert.Equal(t, test.out.expectedConnReusedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnReused.Count(), "Test [%d] incorrect number of reused connections to adapter", i) + assert.Equal(t, test.out.expectedConnCreatedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnCreated.Count(), "Test [%d] incorrect number of new connections to adapter created", i) + assert.Equal(t, test.out.expectedConnWaitTime.Nanoseconds(), m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnWaitTime.Sum(), "Test [%d] incorrect wait time in connection to adapter", i) + } +} + func TestNewMetricsWithDisabledConfig(t *testing.T) { registry := metrics.NewRegistry() m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true}) diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go index 514fbac1015..8133bc739a0 100644 --- a/pbsmetrics/metrics.go +++ b/pbsmetrics/metrics.go @@ -301,6 +301,8 @@ type MetricsEngine interface { RecordLegacyImps(labels Labels, numImps int) // RecordImps for the legacy engine RecordRequestTime(labels Labels, length time.Duration) // ignores adapter. only statusOk and statusErr fom status RecordAdapterRequest(labels AdapterLabels) + RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) + RecordDNSTime(dnsLookupTime time.Duration) RecordAdapterPanic(labels AdapterLabels) // This records whether or not a bid of a particular type uses `adm` or `nurl`. // Since the legacy endpoints don't have a bid type, it can only count bids from OpenRTB and AMP. diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go index 6c263f0af4d..42a2d1b4c8f 100644 --- a/pbsmetrics/metrics_mock.go +++ b/pbsmetrics/metrics_mock.go @@ -52,6 +52,16 @@ func (me *MetricsEngineMock) RecordAdapterRequest(labels AdapterLabels) { me.Called(labels) } +// RecordAdapterConnections mock +func (me *MetricsEngineMock) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { + me.Called(bidderName, connWasReused, connWaitTime) +} + +// RecordDNSTime mock +func (me *MetricsEngineMock) RecordDNSTime(dnsLookupTime time.Duration) { + me.Called(dnsLookupTime) +} + // RecordAdapterBidReceived mock func (me *MetricsEngineMock) RecordAdapterBidReceived(labels AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) { me.Called(labels, bidType, hasAdm) diff --git a/pbsmetrics/prometheus/preload.go b/pbsmetrics/prometheus/preload.go index ef1d300c4df..4f62a18aae9 100644 --- a/pbsmetrics/prometheus/preload.go +++ b/pbsmetrics/prometheus/preload.go @@ -85,6 +85,20 @@ func preloadLabelValues(m *Metrics) { hasBidsLabel: boolValues, }) + if !m.metricsDisabled.AdapterConnectionMetrics { + preloadLabelValuesForCounter(m.adapterCreatedConnections, map[string][]string{ + adapterLabel: adapterValues, + }) + + preloadLabelValuesForCounter(m.adapterReusedConnections, map[string][]string{ + adapterLabel: adapterValues, + }) + + preloadLabelValuesForHistogram(m.adapterConnectionWaitTime, map[string][]string{ + adapterLabel: adapterValues, + }) + } + preloadLabelValuesForHistogram(m.adapterRequestsTimer, map[string][]string{ adapterLabel: adapterValues, }) diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go index d94c4d78f62..b42399b2a62 100644 --- a/pbsmetrics/prometheus/prometheus.go +++ b/pbsmetrics/prometheus/prometheus.go @@ -29,23 +29,29 @@ type Metrics struct { storedImpressionsCacheResult *prometheus.CounterVec storedRequestCacheResult *prometheus.CounterVec timeoutNotifications *prometheus.CounterVec + dnsLookupTimer prometheus.Histogram privacyCCPA *prometheus.CounterVec privacyCOPPA *prometheus.CounterVec privacyLMT *prometheus.CounterVec privacyTCF *prometheus.CounterVec // Adapter Metrics - adapterBids *prometheus.CounterVec - adapterCookieSync *prometheus.CounterVec - adapterErrors *prometheus.CounterVec - adapterPanics *prometheus.CounterVec - adapterPrices *prometheus.HistogramVec - adapterRequests *prometheus.CounterVec - adapterRequestsTimer *prometheus.HistogramVec - adapterUserSync *prometheus.CounterVec + adapterBids *prometheus.CounterVec + adapterCookieSync *prometheus.CounterVec + adapterErrors *prometheus.CounterVec + adapterPanics *prometheus.CounterVec + adapterPrices *prometheus.HistogramVec + adapterRequests *prometheus.CounterVec + adapterRequestsTimer *prometheus.HistogramVec + adapterUserSync *prometheus.CounterVec + adapterReusedConnections *prometheus.CounterVec + adapterCreatedConnections *prometheus.CounterVec + adapterConnectionWaitTime *prometheus.HistogramVec // Account Metrics accountRequests *prometheus.CounterVec + + metricsDisabled config.DisabledMetrics } const ( @@ -97,14 +103,15 @@ const ( ) // NewMetrics initializes a new Prometheus metrics instance with preloaded label values. -func NewMetrics(cfg config.PrometheusMetrics) *Metrics { - requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} +func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMetrics) *Metrics { + standardTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1} cacheWriteTimeBuckets := []float64{0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1} priceBuckets := []float64{250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000} queuedRequestTimeBuckets := []float64{0, 1, 5, 30, 60, 120, 180, 240, 300} metrics := Metrics{} metrics.Registry = prometheus.NewRegistry() + metrics.metricsDisabled = disabledMetrics metrics.connectionsClosed = newCounterWithoutLabels(cfg, metrics.Registry, "connections_closed", @@ -132,7 +139,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "impressions_requests_legacy", "Count of requested impressions to Prebid Server using the legacy endpoint.") - metrics.prebidCacheWriteTimer = newHistogram(cfg, metrics.Registry, + metrics.prebidCacheWriteTimer = newHistogramVec(cfg, metrics.Registry, "prebidcache_write_time_seconds", "Seconds to write to Prebid Cache labeled by success or failure. Failure timing is limited by Prebid Server enforced timeouts.", []string{successLabel}, @@ -143,11 +150,11 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of total requests to Prebid Server labeled by type and status.", []string{requestTypeLabel, requestStatusLabel}) - metrics.requestsTimer = newHistogram(cfg, metrics.Registry, + metrics.requestsTimer = newHistogramVec(cfg, metrics.Registry, "request_time_seconds", "Seconds to resolve successful Prebid Server requests labeled by type.", []string{requestTypeLabel}, - requestTimeBuckets) + standardTimeBuckets) metrics.requestsWithoutCookie = newCounter(cfg, metrics.Registry, "requests_without_cookie", @@ -169,6 +176,11 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of timeout notifications triggered, and if they were successfully sent.", []string{successLabel}) + metrics.dnsLookupTimer = newHistogram(cfg, metrics.Registry, + "dns_lookup_time", + "Seconds to resolve DNS", + standardTimeBuckets) + metrics.privacyCCPA = newCounter(cfg, metrics.Registry, "privacy_ccpa", "Count of total requests to Prebid Server where CCPA was provided by source and opt-out .", @@ -209,7 +221,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of panics labeled by adapter.", []string{adapterLabel}) - metrics.adapterPrices = newHistogram(cfg, metrics.Registry, + metrics.adapterPrices = newHistogramVec(cfg, metrics.Registry, "adapter_prices", "Monetary value of the bids labeled by adapter.", []string{adapterLabel}, @@ -220,11 +232,29 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of requests labeled by adapter, if has a cookie, and if it resulted in bids.", []string{adapterLabel, cookieLabel, hasBidsLabel}) - metrics.adapterRequestsTimer = newHistogram(cfg, metrics.Registry, + if !metrics.metricsDisabled.AdapterConnectionMetrics { + metrics.adapterCreatedConnections = newCounter(cfg, metrics.Registry, + "adapter_connection_created", + "Count that keeps track of new connections when contacting adapter bidder endpoints.", + []string{adapterLabel}) + + metrics.adapterReusedConnections = newCounter(cfg, metrics.Registry, + "adapter_connection_reused", + "Count that keeps track of reused connections when contacting adapter bidder endpoints.", + []string{adapterLabel}) + + metrics.adapterConnectionWaitTime = newHistogramVec(cfg, metrics.Registry, + "adapter_connection_wait", + "Seconds from when the connection was requested until it is either created or reused", + []string{adapterLabel}, + standardTimeBuckets) + } + + metrics.adapterRequestsTimer = newHistogramVec(cfg, metrics.Registry, "adapter_request_time_seconds", "Seconds to resolve each successful request labeled by adapter.", []string{adapterLabel}, - requestTimeBuckets) + standardTimeBuckets) metrics.adapterUserSync = newCounter(cfg, metrics.Registry, "adapter_user_sync", @@ -236,7 +266,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics { "Count of total requests to Prebid Server labeled by account.", []string{accountLabel}) - metrics.requestsQueueTimer = newHistogram(cfg, metrics.Registry, + metrics.requestsQueueTimer = newHistogramVec(cfg, metrics.Registry, "request_queue_time", "Seconds request was waiting in queue", []string{requestTypeLabel, requestStatusLabel}, @@ -271,7 +301,7 @@ func newCounterWithoutLabels(cfg config.PrometheusMetrics, registry *prometheus. return counter } -func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec { +func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec { opts := prometheus.HistogramOpts{ Namespace: cfg.Namespace, Subsystem: cfg.Subsystem, @@ -284,6 +314,19 @@ func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, n return histogram } +func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, buckets []float64) prometheus.Histogram { + opts := prometheus.HistogramOpts{ + Namespace: cfg.Namespace, + Subsystem: cfg.Subsystem, + Name: name, + Help: help, + Buckets: buckets, + } + histogram := prometheus.NewHistogram(opts) + registry.MustRegister(histogram) + return histogram +} + func (m *Metrics) RecordConnectionAccept(success bool) { if success { m.connectionsOpened.Inc() @@ -359,6 +402,32 @@ func (m *Metrics) RecordAdapterRequest(labels pbsmetrics.AdapterLabels) { } } +// Keeps track of created and reused connections to adapter bidders and the time from the +// connection request, to the connection creation, or reuse from the pool across all engines +func (m *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { + if m.metricsDisabled.AdapterConnectionMetrics { + return + } + + if connWasReused { + m.adapterReusedConnections.With(prometheus.Labels{ + adapterLabel: string(adapterName), + }).Inc() + } else { + m.adapterCreatedConnections.With(prometheus.Labels{ + adapterLabel: string(adapterName), + }).Inc() + } + + m.adapterConnectionWaitTime.With(prometheus.Labels{ + adapterLabel: string(adapterName), + }).Observe(connWaitTime.Seconds()) +} + +func (m *Metrics) RecordDNSTime(dnsLookupTime time.Duration) { + m.dnsLookupTimer.Observe(dnsLookupTime.Seconds()) +} + func (m *Metrics) RecordAdapterPanic(labels pbsmetrics.AdapterLabels) { m.adapterPanics.With(prometheus.Labels{ adapterLabel: string(labels.Adapter), diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go index b722ab28b5c..b6153b16278 100644 --- a/pbsmetrics/prometheus/prometheus_test.go +++ b/pbsmetrics/prometheus/prometheus_test.go @@ -1,6 +1,7 @@ package prometheusmetrics import ( + "fmt" "testing" "time" @@ -17,7 +18,7 @@ func createMetricsForTesting() *Metrics { Port: 8080, Namespace: "prebid", Subsystem: "server", - }) + }, config.DisabledMetrics{}) } func TestMetricCountGatekeeping(t *testing.T) { @@ -61,7 +62,7 @@ func TestMetricCountGatekeeping(t *testing.T) { // Verify Per-Adapter Cardinality // - This assertion provides a warning for newly added adapter metrics. Threre are 40+ adapters which makes the // cost of new per-adapter metrics rather expensive. Thought should be given when adding new per-adapter metrics. - assert.True(t, perAdapterCardinalityCount <= 22, "Per-Adapter Cardinality") + assert.True(t, perAdapterCardinalityCount <= 27, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount) } func TestConnectionMetrics(t *testing.T) { @@ -944,6 +945,175 @@ func TestTimeoutNotifications(t *testing.T) { } +func TestRecordDNSTime(t *testing.T) { + type testIn struct { + dnsLookupDuration time.Duration + } + type testOut struct { + expDuration float64 + expCount uint64 + } + testCases := []struct { + description string + in testIn + out testOut + }{ + { + description: "Five second DNS lookup time", + in: testIn{ + dnsLookupDuration: time.Second * 5, + }, + out: testOut{ + expDuration: 5, + expCount: 1, + }, + }, + { + description: "Zero DNS lookup time", + in: testIn{}, + out: testOut{ + expDuration: 0, + expCount: 1, + }, + }, + } + for i, test := range testCases { + pm := createMetricsForTesting() + pm.RecordDNSTime(test.in.dnsLookupDuration) + + m := dto.Metric{} + pm.dnsLookupTimer.Write(&m) + histogram := *m.GetHistogram() + + assert.Equal(t, test.out.expCount, histogram.GetSampleCount(), "[%d] Incorrect number of histogram entries. Desc: %s\n", i, test.description) + assert.Equal(t, test.out.expDuration, histogram.GetSampleSum(), "[%d] Incorrect number of histogram cumulative values. Desc: %s\n", i, test.description) + } +} + +func TestRecordAdapterConnections(t *testing.T) { + + type testIn struct { + adapterName openrtb_ext.BidderName + connWasReused bool + connWait time.Duration + } + + type testOut struct { + expectedConnReusedCount int64 + expectedConnCreatedCount int64 + expectedConnWaitCount uint64 + expectedConnWaitTime float64 + } + + testCases := []struct { + description string + in testIn + out testOut + }{ + { + description: "[1] Successful, new connection created, was idle, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: false, + connWait: time.Second * 5, + }, + out: testOut{ + expectedConnReusedCount: 0, + expectedConnCreatedCount: 1, + expectedConnWaitCount: 1, + expectedConnWaitTime: 5, + }, + }, + { + description: "[2] Successful, new connection created, not idle, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: false, + connWait: time.Second * 4, + }, + out: testOut{ + expectedConnReusedCount: 0, + expectedConnCreatedCount: 1, + expectedConnWaitCount: 1, + expectedConnWaitTime: 4, + }, + }, + { + description: "[3] Successful, was reused, was idle, no connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: true, + }, + out: testOut{ + expectedConnReusedCount: 1, + expectedConnCreatedCount: 0, + expectedConnWaitCount: 1, + expectedConnWaitTime: 0, + }, + }, + { + description: "[4] Successful, was reused, not idle, has connection wait", + in: testIn{ + adapterName: openrtb_ext.BidderAppnexus, + connWasReused: true, + connWait: time.Second * 5, + }, + out: testOut{ + expectedConnReusedCount: 1, + expectedConnCreatedCount: 0, + expectedConnWaitCount: 1, + expectedConnWaitTime: 5, + }, + }, + } + + for i, test := range testCases { + m := createMetricsForTesting() + assertDesciptions := []string{ + fmt.Sprintf("[%d] Metric: adapterReusedConnections; Desc: %s", i+1, test.description), + fmt.Sprintf("[%d] Metric: adapterCreatedConnections; Desc: %s", i+1, test.description), + fmt.Sprintf("[%d] Metric: adapterWaitConnectionCount; Desc: %s", i+1, test.description), + fmt.Sprintf("[%d] Metric: adapterWaitConnectionTime; Desc: %s", i+1, test.description), + } + + m.RecordAdapterConnections(test.in.adapterName, test.in.connWasReused, test.in.connWait) + + // Assert number of reused connections + assertCounterVecValue(t, + assertDesciptions[0], + "adapter_connection_reused", + m.adapterReusedConnections, + float64(test.out.expectedConnReusedCount), + prometheus.Labels{adapterLabel: string(test.in.adapterName)}) + + // Assert number of new created connections + assertCounterVecValue(t, + assertDesciptions[1], + "adapter_connection_created", + m.adapterCreatedConnections, + float64(test.out.expectedConnCreatedCount), + prometheus.Labels{adapterLabel: string(test.in.adapterName)}) + + // Assert connection wait time + histogram := getHistogramFromHistogramVec(m.adapterConnectionWaitTime, adapterLabel, string(test.in.adapterName)) + assert.Equal(t, test.out.expectedConnWaitCount, histogram.GetSampleCount(), assertDesciptions[2]) + assert.Equal(t, test.out.expectedConnWaitTime, histogram.GetSampleSum(), assertDesciptions[3]) + } +} + +func TestDisableAdapterConnections(t *testing.T) { + prometheusMetrics := NewMetrics(config.PrometheusMetrics{ + Port: 8080, + Namespace: "prebid", + Subsystem: "server", + }, config.DisabledMetrics{AdapterConnectionMetrics: true}) + + // Assert counter vector was not initialized + assert.Nil(t, prometheusMetrics.adapterReusedConnections, "Counter Vector adapterReusedConnections should be nil") + assert.Nil(t, prometheusMetrics.adapterCreatedConnections, "Counter Vector adapterCreatedConnections should be nil") + assert.Nil(t, prometheusMetrics.adapterConnectionWaitTime, "Counter Vector adapterConnectionWaitTime should be nil") +} + func TestRecordRequestPrivacy(t *testing.T) { m := createMetricsForTesting() From f1582a494e407635544be416632e26c76d2b1881 Mon Sep 17 00:00:00 2001 From: PubMatic-OpenWrap Date: Mon, 27 Jul 2020 18:53:23 +0530 Subject: [PATCH 150/318] Pubmatic: Support for video duration and primary category (#1384) * Adding suport for video duration and primary category in pubmatic adapter * Adding code review changes for PR-1384 * Adding changes for syntaxNode suggestion Co-authored-by: Isha Bharti --- adapters/pubmatic/pubmatic.go | 67 ++++++++++++------- adapters/pubmatic/pubmatic_test.go | 33 ++++----- .../pubmatictest/exemplary/video.json | 21 ++++-- 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go index aae115386d0..795655048b4 100644 --- a/adapters/pubmatic/pubmatic.go +++ b/adapters/pubmatic/pubmatic.go @@ -21,7 +21,6 @@ import ( ) const MAX_IMPRESSIONS_PUBMATIC = 30 -const bidTypeExtKey = "BidType" type PubmaticAdapter struct { http *adapters.HTTPAdapter @@ -48,6 +47,15 @@ type pubmaticParams struct { Keywords map[string]string `json:"keywords,omitempty"` } +type pubmaticBidExtVideo struct { + Duration *int `json:"duration,omitempty"` +} + +type pubmaticBidExt struct { + BidType *int `json:"BidType,omitempty"` + VideoCreativeInfo *pubmaticBidExtVideo `json:"video,omitempty"` +} + const ( INVALID_PARAMS = "Invalid BidParam" MISSING_PUBID = "Missing PubID" @@ -289,7 +297,11 @@ func (a *PubmaticAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder DealId: bid.DealID, } - mediaType := getBidType(bid.Ext) + var bidExt pubmaticBidExt + mediaType := openrtb_ext.BidTypeBanner + if err := json.Unmarshal(bid.Ext, &bidExt); err == nil { + mediaType = getBidType(&bidExt) + } pbid.CreativeMediaType = string(mediaType) bids = append(bids, &pbid) @@ -549,9 +561,24 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb.BidRequest, external for _, sb := range bidResp.SeatBid { for i := 0; i < len(sb.Bid); i++ { bid := sb.Bid[i] + impVideo := &openrtb_ext.ExtBidPrebidVideo{} + + if len(bid.Cat) > 1 { + bid.Cat = bid.Cat[0:1] + } + + var bidExt *pubmaticBidExt + bidType := openrtb_ext.BidTypeBanner + if err := json.Unmarshal(bid.Ext, &bidExt); err == nil && bidExt != nil { + if bidExt.VideoCreativeInfo != nil && bidExt.VideoCreativeInfo.Duration != nil { + impVideo.Duration = *bidExt.VideoCreativeInfo.Duration + } + bidType = getBidType(bidExt) + } bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: getBidType(bid.Ext), + Bid: &bid, + BidType: bidType, + BidVideo: impVideo, }) } @@ -560,28 +587,20 @@ func (a *PubmaticAdapter) MakeBids(internalRequest *openrtb.BidRequest, external } // getBidType returns the bid type specified in the response bid.ext -func getBidType(bidExt json.RawMessage) openrtb_ext.BidType { +func getBidType(bidExt *pubmaticBidExt) openrtb_ext.BidType { // setting "banner" as the default bid type bidType := openrtb_ext.BidTypeBanner - if bidExt != nil { - bidExtMap := make(map[string]interface{}) - extbyte, err := json.Marshal(bidExt) - if err == nil { - err = json.Unmarshal(extbyte, &bidExtMap) - if err == nil && bidExtMap[bidTypeExtKey] != nil { - bidTypeVal := int(bidExtMap[bidTypeExtKey].(float64)) - switch bidTypeVal { - case 0: - bidType = openrtb_ext.BidTypeBanner - case 1: - bidType = openrtb_ext.BidTypeVideo - case 2: - bidType = openrtb_ext.BidTypeNative - default: - // default value is banner - bidType = openrtb_ext.BidTypeBanner - } - } + if bidExt != nil && bidExt.BidType != nil { + switch *bidExt.BidType { + case 0: + bidType = openrtb_ext.BidTypeBanner + case 1: + bidType = openrtb_ext.BidTypeVideo + case 2: + bidType = openrtb_ext.BidTypeNative + default: + // default value is banner + bidType = openrtb_ext.BidTypeBanner } } return bidType diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go index be086f5adf1..5dd236d9742 100644 --- a/adapters/pubmatic/pubmatic_test.go +++ b/adapters/pubmatic/pubmatic_test.go @@ -674,18 +674,18 @@ func TestPubmaticSampleRequest(t *testing.T) { } func TestGetBidTypeVideo(t *testing.T) { - extJSON := `{"BidType":1}` - extrm := json.RawMessage(extJSON) - actualBidTypeValue := getBidType(extrm) + pubmaticExt := new(pubmaticBidExt) + pubmaticExt.BidType = new(int) + *pubmaticExt.BidType = 1 + actualBidTypeValue := getBidType(pubmaticExt) if actualBidTypeValue != openrtb_ext.BidTypeVideo { t.Errorf("Expected Bid Type value was: %v, actual value is: %v", openrtb_ext.BidTypeVideo, actualBidTypeValue) } } func TestGetBidTypeForMissingBidTypeExt(t *testing.T) { - extJSON := `{}` - extrm := json.RawMessage(extJSON) - actualBidTypeValue := getBidType(extrm) + pubmaticExt := pubmaticBidExt{} + actualBidTypeValue := getBidType(&pubmaticExt) // banner is the default bid type when no bidType key is present in the bid.ext if actualBidTypeValue != "banner" { t.Errorf("Expected Bid Type value was: banner, actual value is: %v", actualBidTypeValue) @@ -693,27 +693,30 @@ func TestGetBidTypeForMissingBidTypeExt(t *testing.T) { } func TestGetBidTypeBanner(t *testing.T) { - extJSON := `{"BidType":0}` - extrm := json.RawMessage(extJSON) - actualBidTypeValue := getBidType(extrm) + pubmaticExt := new(pubmaticBidExt) + pubmaticExt.BidType = new(int) + *pubmaticExt.BidType = 0 + actualBidTypeValue := getBidType(pubmaticExt) if actualBidTypeValue != openrtb_ext.BidTypeBanner { t.Errorf("Expected Bid Type value was: %v, actual value is: %v", openrtb_ext.BidTypeBanner, actualBidTypeValue) } } func TestGetBidTypeNative(t *testing.T) { - extJSON := `{"BidType":2}` - extrm := json.RawMessage(extJSON) - actualBidTypeValue := getBidType(extrm) + pubmaticExt := new(pubmaticBidExt) + pubmaticExt.BidType = new(int) + *pubmaticExt.BidType = 2 + actualBidTypeValue := getBidType(pubmaticExt) if actualBidTypeValue != openrtb_ext.BidTypeNative { t.Errorf("Expected Bid Type value was: %v, actual value is: %v", openrtb_ext.BidTypeNative, actualBidTypeValue) } } func TestGetBidTypeForUnsupportedCode(t *testing.T) { - extJSON := `{"BidType":99}` - extrm := json.RawMessage(extJSON) - actualBidTypeValue := getBidType(extrm) + pubmaticExt := new(pubmaticBidExt) + pubmaticExt.BidType = new(int) + *pubmaticExt.BidType = 99 + actualBidTypeValue := getBidType(pubmaticExt) if actualBidTypeValue != openrtb_ext.BidTypeBanner { t.Errorf("Expected Bid Type value was: %v, actual value is: %v", openrtb_ext.BidTypeBanner, actualBidTypeValue) } diff --git a/adapters/pubmatic/pubmatictest/exemplary/video.json b/adapters/pubmatic/pubmatictest/exemplary/video.json index 25ed366ee05..4c874535a35 100644 --- a/adapters/pubmatic/pubmatictest/exemplary/video.json +++ b/adapters/pubmatic/pubmatictest/exemplary/video.json @@ -112,10 +112,14 @@ "h": 250, "w": 300, "dealid":"test deal", + "cat" : ["IAB-1", "IAB-2"], "ext": { "dspid": 6, "deal_channel": 1, - "BidType": 1 + "BidType": 1, + "video" : { + "duration" : 5 + } } }] } @@ -139,6 +143,9 @@ "adid": "29681110", "adm": "some-test-ad", "adomain": ["pubmatic.com"], + "cat": [ + "IAB-1" + ], "crid": "29681110", "w": 300, "h": 250, @@ -146,12 +153,18 @@ "ext": { "dspid": 6, "deal_channel": 1, - "BidType": 1 + "BidType": 1, + "video" : { + "duration" : 5 + } } }, - "type": "video" + "type": "video", + "video" :{ + "duration" : 5 + } } ] } ] - } \ No newline at end of file + } From a857e6868e0de3445dc0c65cf6522d290ce1df23 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 29 Jul 2020 12:03:31 -0400 Subject: [PATCH 151/318] Add IPv6 Non-Public Network (#1417) --- config/config.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index a82dbb5edf7..8545523d238 100755 --- a/config/config.go +++ b/config/config.go @@ -896,13 +896,14 @@ func SetupViper(v *viper.Viper, filename string) { /* Loopback: 127.0.0.0/8 /* /* IPv6 - /* Loopback: ::1/128 - /* Unique Local: fc00::/7 - /* Link Local: fe80::/10 - /* Multicast: ff00::/8 + /* Loopback: ::1/128 + /* Documentation: 2001:db8::/32 + /* Unique Local: fc00::/7 + /* Link Local: fe80::/10 + /* Multicast: ff00::/8 */ v.SetDefault("request_validation.ipv4_private_networks", []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "127.0.0.0/8"}) - v.SetDefault("request_validation.ipv6_private_networks", []string{"::1/128", "fc00::/7", "fe80::/10", "ff00::/8"}) + v.SetDefault("request_validation.ipv6_private_networks", []string{"::1/128", "fc00::/7", "fe80::/10", "ff00::/8", "2001:db8::/32"}) // Set environment variable support: v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) From 3fa47ab2218a1fdcb6d9a6fc5edd3fff5ad10318 Mon Sep 17 00:00:00 2001 From: susyt Date: Wed, 29 Jul 2020 14:18:29 -0700 Subject: [PATCH 152/318] GumGum: adds support for video (#1408) --- adapters/gumgum/gumgum.go | 57 ++++++++-- .../gumgum/gumgumtest/exemplary/video.json | 106 ++++++++++++++++++ .../gumgum/gumgumtest/params/race/video.json | 3 + .../supplemental/missing-video-params.json | 36 ++++++ static/bidder-info/gumgum.yaml | 1 + 5 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 adapters/gumgum/gumgumtest/exemplary/video.json create mode 100644 adapters/gumgum/gumgumtest/params/race/video.json create mode 100644 adapters/gumgum/gumgumtest/supplemental/missing-video-params.json diff --git a/adapters/gumgum/gumgum.go b/adapters/gumgum/gumgum.go index 84a008d1891..490979091a4 100644 --- a/adapters/gumgum/gumgum.go +++ b/adapters/gumgum/gumgum.go @@ -8,12 +8,16 @@ import ( "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "net/http" + "strconv" + "strings" ) +// GumGumAdapter implements Bidder interface. type GumGumAdapter struct { URI string } +// MakeRequests makes the HTTP requests which should be made to fetch bids. func (g *GumGumAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var validImps []openrtb.Imp var trackingId string @@ -26,15 +30,21 @@ func (g *GumGumAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapt zone, err := preprocess(&imp) if err != nil { errs = append(errs, err) - } else { - if request.Imp[i].Banner != nil { - bannerCopy := *request.Imp[i].Banner - if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 { - format := bannerCopy.Format[0] - bannerCopy.W = &(format.W) - bannerCopy.H = &(format.H) - } - request.Imp[i].Banner = &bannerCopy + } else if request.Imp[i].Banner != nil { + bannerCopy := *request.Imp[i].Banner + if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 { + format := bannerCopy.Format[0] + bannerCopy.W = &(format.W) + bannerCopy.H = &(format.H) + } + request.Imp[i].Banner = &bannerCopy + validImps = append(validImps, request.Imp[i]) + trackingId = zone + } else if request.Imp[i].Video != nil { + err := validateVideoParams(request.Imp[i].Video) + if err != nil { + errs = append(errs, err) + } else { validImps = append(validImps, request.Imp[i]) trackingId = zone } @@ -70,6 +80,7 @@ func (g *GumGumAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapt }}, errs } +// MakeBids unpacks the server's response into Bids. func (g *GumGumAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { if response.StatusCode == http.StatusNoContent { return nil, nil @@ -98,12 +109,19 @@ func (g *GumGumAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRe for _, sb := range bidResp.SeatBid { for i := range sb.Bid { + mediaType := getMediaTypeForImpID(sb.Bid[i].ImpID, internalRequest.Imp) + if mediaType == openrtb_ext.BidTypeVideo { + price := strconv.FormatFloat(sb.Bid[i].Price, 'f', -1, 64) + sb.Bid[i].AdM = strings.Replace(sb.Bid[i].AdM, "${AUCTION_PRICE}", price, -1) + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &sb.Bid[i], - BidType: openrtb_ext.BidTypeBanner, + BidType: mediaType, }) } } + return bidResponse, errs } @@ -128,6 +146,25 @@ func preprocess(imp *openrtb.Imp) (string, error) { return zone, nil } +func getMediaTypeForImpID(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + for _, imp := range imps { + if imp.ID == impID && imp.Banner != nil { + return openrtb_ext.BidTypeBanner + } + } + return openrtb_ext.BidTypeVideo +} + +func validateVideoParams(video *openrtb.Video) (err error) { + if video.W == 0 || video.H == 0 || video.MinDuration == 0 || video.MaxDuration == 0 || video.Placement == 0 || video.Linearity == 0 { + return &errortypes.BadInput{ + Message: "Invalid or missing video field(s)", + } + } + return nil +} + +// NewGumGumBidder configures bidder endpoint. func NewGumGumBidder(endpoint string) *GumGumAdapter { return &GumGumAdapter{ URI: endpoint, diff --git a/adapters/gumgum/gumgumtest/exemplary/video.json b/adapters/gumgum/gumgumtest/exemplary/video.json new file mode 100644 index 00000000000..ea76c733f34 --- /dev/null +++ b/adapters/gumgum/gumgumtest/exemplary/video.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 1, + "maxduration": 2, + "protocols": [ + 1, + 2 + ], + "w": 640, + "h": 480, + "startdelay": 1, + "placement": 1, + "linearity": 1 + }, + "ext": { + "bidder": { + "zone": "ggumtest" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g2.gumgum.com/providers/prbds2s/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 1, + "maxduration": 2, + "protocols": [ + 1, + 2 + ], + "w": 640, + "h": 480, + "startdelay": 1, + "placement": 1, + "linearity": 1 + }, + "ext": { + "bidder": { + "zone": "ggumtest" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "id": "15da721e-940a-4db6-8621-a1f93140b21b", + "impid": "video1", + "price": 15, + "adid": "59082", + "adm": "\n \n \n GumGum Video\n \n \n \n \n \n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "cid": "3579", + "crid": "59082" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "15da721e-940a-4db6-8621-a1f93140b21b", + "impid": "video1", + "price": 15, + "adid": "59082", + "adm": "\n \n \n GumGum Video\n \n \n \n \n \n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "cid": "3579", + "crid": "59082" + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/gumgum/gumgumtest/params/race/video.json b/adapters/gumgum/gumgumtest/params/race/video.json new file mode 100644 index 00000000000..3ed284384d3 --- /dev/null +++ b/adapters/gumgum/gumgumtest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "zone": "dc9d6be1" +} \ No newline at end of file diff --git a/adapters/gumgum/gumgumtest/supplemental/missing-video-params.json b/adapters/gumgum/gumgumtest/supplemental/missing-video-params.json new file mode 100644 index 00000000000..b2475cd7bb4 --- /dev/null +++ b/adapters/gumgum/gumgumtest/supplemental/missing-video-params.json @@ -0,0 +1,36 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 1, + 2 + ], + "w": 640, + "h": 480, + "startdelay": 1, + "placement": 1, + "linearity": 1 + }, + "ext": { + "bidder": { + "zone": "ggumtest" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Invalid or missing video field(s)", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/static/bidder-info/gumgum.yaml b/static/bidder-info/gumgum.yaml index 0feca7cdf73..6ba563b4c1c 100644 --- a/static/bidder-info/gumgum.yaml +++ b/static/bidder-info/gumgum.yaml @@ -4,3 +4,4 @@ capabilities: site: mediaTypes: - banner + - video \ No newline at end of file From 551faadb0502cef9d7dda5b90655f348b3a7bc19 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Thu, 30 Jul 2020 09:23:27 -0700 Subject: [PATCH 153/318] OpenX adapter: pass optional platform (PBID-598) (#1421) --- adapters/openx/openx.go | 4 +++- .../openxtest/exemplary/optional-params.json | 4 +++- docs/bidders/openx.md | 7 ++++-- openrtb_ext/imp_openx.go | 1 + static/bidder-params/openx.json | 22 +++++++++++++++++-- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go index ca88b18bdb8..c2a42adf295 100644 --- a/adapters/openx/openx.go +++ b/adapters/openx/openx.go @@ -22,7 +22,8 @@ type openxImpExt struct { } type openxReqExt struct { - DelDomain string `json:"delDomain"` + DelDomain string `json:"delDomain,omitempty"` + Platform string `json:"platform,omitempty"` BidderConfig string `json:"bc"` } @@ -125,6 +126,7 @@ func preprocess(imp *openrtb.Imp, reqExt *openxReqExt) error { } reqExt.DelDomain = openxExt.DelDomain + reqExt.Platform = openxExt.Platform imp.TagID = openxExt.Unit imp.BidFloor = openxExt.CustomFloor diff --git a/adapters/openx/openxtest/exemplary/optional-params.json b/adapters/openx/openxtest/exemplary/optional-params.json index 225559875a8..e8fcf394b8b 100644 --- a/adapters/openx/openxtest/exemplary/optional-params.json +++ b/adapters/openx/openxtest/exemplary/optional-params.json @@ -11,6 +11,7 @@ "bidder": { "unit": "539439964", "delDomain": "se-demo-d.openx.net", + "platform": "PLATFORM", "customFloor": 0.1, "customParams": {"foo": "bar"} } @@ -40,7 +41,8 @@ ], "ext": { "bc": "hb_pbs_1.0.0", - "delDomain": "se-demo-d.openx.net" + "delDomain": "se-demo-d.openx.net", + "platform": "PLATFORM" } } }, diff --git a/docs/bidders/openx.md b/docs/bidders/openx.md index c366db3ab61..54a0a5b1e72 100644 --- a/docs/bidders/openx.md +++ b/docs/bidders/openx.md @@ -5,10 +5,13 @@ OpenX supports the following parameters: | property | type | required? | description | example | |----------|------|-----------|-------------|---------| | unit | string | required | The ad unit id | "10092842" | -| delDomain | string | required | The delivery domain for the customer | "sademo-d.openx.net" | +| delDomain | string | required\* | The delivery domain for the customer | "sademo-d.openx.net" | +| platform | uuid | required\* | The platform id for the customer | "a3aece0c-9e80-4316-8deb-faf804779bd1" | | customFloor | number | optional | The minimum CPM price in USD | 1.50 - sets a $1.50 floor | | customParams | object | optional | User-defined targeting key-value pairs | {key1: "v1", key2: ["v2","v3"]} | +\* At least one of `delDomain` or `platform` parameters is required. + If you have any questions regarding setting up, please reach out to your account manager or @@ -59,4 +62,4 @@ If you have any questions regarding setting up, please reach out to your account }, } } -``` \ No newline at end of file +``` diff --git a/openrtb_ext/imp_openx.go b/openrtb_ext/imp_openx.go index e63595b0912..2625cb3802d 100644 --- a/openrtb_ext/imp_openx.go +++ b/openrtb_ext/imp_openx.go @@ -3,6 +3,7 @@ package openrtb_ext // ExtImpOpenx defines the contract for bidrequest.imp[i].ext.openx type ExtImpOpenx struct { Unit string `json:"unit"` + Platform string `json:"platform"` DelDomain string `json:"delDomain"` CustomFloor float64 `json:"customFloor"` CustomParams map[string]interface{} `json:"customParams"` diff --git a/static/bidder-params/openx.json b/static/bidder-params/openx.json index 93a672ed629..6dbd10178e4 100644 --- a/static/bidder-params/openx.json +++ b/static/bidder-params/openx.json @@ -16,6 +16,11 @@ "pattern": "\\.[a-zA-Z]{2,3}$", "format": "hostname" }, + "platform": { + "type": "string", + "description": "The platform id for the customer.", + "format": "uuid" + }, "customFloor": { "type": "number", "description": "The minimum CPM price in USD.", @@ -26,6 +31,19 @@ "description": "User-defined targeting key-value pairs." } }, - - "required": ["unit", "delDomain"] + "required": [ + "unit" + ], + "anyOf": [ + { + "required": [ + "delDomain" + ] + }, + { + "required": [ + "platform" + ] + } + ] } From 126455ccfeb3965b5c4a8b49749dbbce144e5317 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:41:26 -0400 Subject: [PATCH 154/318] Adds keyvalue hb_format support (#1414) --- docs/endpoints/openrtb2/auction.md | 3 + exchange/exchange.go | 1 + exchange/targeting.go | 4 + exchange/targeting_test.go | 209 +++++++++++++++++++++++++++++ openrtb_ext/bid.go | 3 + openrtb_ext/request.go | 1 + 6 files changed, 221 insertions(+) diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md index d09216188b8..b532923e793 100644 --- a/docs/endpoints/openrtb2/auction.md +++ b/docs/endpoints/openrtb2/auction.md @@ -173,6 +173,7 @@ to set these params on the response at `response.seatbid[i].bid[j].ext.prebid.ta }, "includewinners": false, // Optional param defaulting to true "includebidderkeys": false // Optional param defaulting to true + "includeformat": false // Optional param defaulting to false } } } @@ -184,6 +185,8 @@ For backwards compatibility the following strings will also be allowed as price One of "includewinners" or "includebidderkeys" must be true (both default to true if unset). If both were false, then no targeting keys would be set, which is better configured by omitting targeting altogether. +The parameter "includeformat" indicates the type of the bid (banner, video, etc) for multiformat requests. It will add the key `hb_format` and/or `hb_format_{bidderName}` as per "includewinners" and "includebidderkeys" above. + MediaType PriceGranularity (PBS-Java only) - when a single OpenRTB request contains multiple impressions with different mediatypes, or a single impression supports multiple formats, the different mediatypes may need different price granularities. If `mediatypepricegranularity` is present, `pricegranularity` would only be used for any mediatypes not specified. ``` diff --git a/exchange/exchange.go b/exchange/exchange.go index 3f0258dd3c1..5001e495440 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -135,6 +135,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque includeBidderKeys: requestExt.Prebid.Targeting.IncludeBidderKeys, includeCacheBids: shouldCacheBids, includeCacheVast: shouldCacheVAST, + includeFormat: requestExt.Prebid.Targeting.IncludeFormat, } targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() } diff --git a/exchange/targeting.go b/exchange/targeting.go index 994ad9e7c81..dca57653b44 100644 --- a/exchange/targeting.go +++ b/exchange/targeting.go @@ -22,6 +22,7 @@ type targetData struct { includeBidderKeys bool includeCacheBids bool includeCacheVast bool + includeFormat bool // cacheHost and cachePath exist to supply cache host and path as targeting parameters cacheHost string cachePath string @@ -53,6 +54,9 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi if vastID, ok := auc.vastCacheIds[topBidPerBidder.bid]; ok { targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, bidderName, isOverallWinner) } + if targData.includeFormat { + targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBidPerBidder.bidType), bidderName, isOverallWinner) + } if targData.cacheHost != "" { targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, bidderName, isOverallWinner) diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 16955e97c5b..11b60db01f3 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -247,3 +247,212 @@ func (f *mockFetcher) GetId(bidder openrtb_ext.BidderName) (string, bool) { func mockServer(w http.ResponseWriter, req *http.Request) { w.Write([]byte("{}")) } + +type TargetingTestData struct { + Description string + TargetData targetData + Auction auction + IsApp bool + CategoryMapping map[string]string + ExpectedBidTargetsByBidder map[string]map[openrtb_ext.BidderName]map[string]string +} + +var bid123 *openrtb.Bid = &openrtb.Bid{ + Price: 1.23, +} + +var bid111 *openrtb.Bid = &openrtb.Bid{ + Price: 1.11, + DealID: "mydeal", +} +var bid084 *openrtb.Bid = &openrtb.Bid{ + Price: 0.84, +} + +var TargetingTests []TargetingTestData = []TargetingTestData{ + { + Description: "Targeting winners only (most basic targeting example)", + TargetData: targetData{ + priceGranularity: openrtb_ext.PriceGranularityFromString("med"), + includeWinners: true, + }, + Auction: auction{ + winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + bid: bid123, + bidType: openrtb_ext.BidTypeBanner, + }, + openrtb_ext.BidderRubicon: { + bid: bid084, + bidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + }, + ExpectedBidTargetsByBidder: map[string]map[openrtb_ext.BidderName]map[string]string{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + "hb_bidder": "appnexus", + "hb_pb": "1.20", + }, + openrtb_ext.BidderRubicon: {}, + }, + }, + }, + { + Description: "Targeting on bidders only", + TargetData: targetData{ + priceGranularity: openrtb_ext.PriceGranularityFromString("med"), + includeBidderKeys: true, + }, + Auction: auction{ + winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + bid: bid123, + bidType: openrtb_ext.BidTypeBanner, + }, + openrtb_ext.BidderRubicon: { + bid: bid084, + bidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + }, + ExpectedBidTargetsByBidder: map[string]map[openrtb_ext.BidderName]map[string]string{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + "hb_bidder_appnexus": "appnexus", + "hb_pb_appnexus": "1.20", + }, + openrtb_ext.BidderRubicon: { + "hb_bidder_rubicon": "rubicon", + "hb_pb_rubicon": "0.80", + }, + }, + }, + }, + { + Description: "Full basic targeting with hd_format", + TargetData: targetData{ + priceGranularity: openrtb_ext.PriceGranularityFromString("med"), + includeWinners: true, + includeBidderKeys: true, + includeFormat: true, + }, + Auction: auction{ + winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + bid: bid123, + bidType: openrtb_ext.BidTypeBanner, + }, + openrtb_ext.BidderRubicon: { + bid: bid084, + bidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + }, + ExpectedBidTargetsByBidder: map[string]map[openrtb_ext.BidderName]map[string]string{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_pb": "1.20", + "hb_pb_appnexus": "1.20", + "hb_format": "banner", + "hb_format_appnexus": "banner", + }, + openrtb_ext.BidderRubicon: { + "hb_bidder_rubicon": "rubicon", + "hb_pb_rubicon": "0.80", + "hb_format_rubicon": "banner", + }, + }, + }, + }, + { + Description: "Cache and deal targeting test", + TargetData: targetData{ + priceGranularity: openrtb_ext.PriceGranularityFromString("med"), + includeBidderKeys: true, + cacheHost: "cache.prebid.com", + cachePath: "cache", + }, + Auction: auction{ + winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + bid: bid123, + bidType: openrtb_ext.BidTypeBanner, + }, + openrtb_ext.BidderRubicon: { + bid: bid111, + bidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + cacheIds: map[*openrtb.Bid]string{ + bid123: "55555", + bid111: "cacheme", + }, + }, + ExpectedBidTargetsByBidder: map[string]map[openrtb_ext.BidderName]map[string]string{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: { + "hb_bidder_appnexus": "appnexus", + "hb_pb_appnexus": "1.20", + "hb_cache_id_appnexus": "55555", + "hb_cache_host_appnex": "cache.prebid.com", + "hb_cache_path_appnex": "cache", + }, + openrtb_ext.BidderRubicon: { + "hb_bidder_rubicon": "rubicon", + "hb_pb_rubicon": "1.10", + "hb_cache_id_rubicon": "cacheme", + "hb_deal_rubicon": "mydeal", + "hb_cache_host_rubico": "cache.prebid.com", + "hb_cache_path_rubico": "cache", + }, + }, + }, + }, +} + +func TestSetTargeting(t *testing.T) { + for _, test := range TargetingTests { + auc := &test.Auction + // Set rounded prices from the auction data + auc.setRoundedPrices(test.TargetData.priceGranularity) + winningBids := make(map[string]*pbsOrtbBid) + // Set winning bids from the auction data + for imp, bidsByBidder := range auc.winningBidsByBidder { + for _, bid := range bidsByBidder { + if winningBid, ok := winningBids[imp]; ok { + if winningBid.bid.Price < bid.bid.Price { + winningBids[imp] = bid + } + } else { + winningBids[imp] = bid + } + } + } + auc.winningBids = winningBids + targData := test.TargetData + targData.setTargeting(auc, test.IsApp, test.CategoryMapping) + for imp, targetsByBidder := range test.ExpectedBidTargetsByBidder { + for bidder, expected := range targetsByBidder { + assert.Equal(t, + expected, + auc.winningBidsByBidder[imp][bidder].bidTargets, + "Test: %s\nTargeting failed for bidder %s on imp %s.", + test.Description, + string(bidder), + imp) + } + } + } + +} diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go index 3b297c7ab5d..09ee375be82 100644 --- a/openrtb_ext/bid.go +++ b/openrtb_ext/bid.go @@ -99,6 +99,9 @@ const ( HbSizeConstantKey TargetingKey = "hb_size" HbDealIDConstantKey TargetingKey = "hb_deal" + // HbFormatKey is the format of the bid. For example, "video", "banner" + HbFormatKey TargetingKey = "hb_format" + // HbCacheKey and HbVastCacheKey store UUIDs which can be used to fetch things from prebid cache. // Callers should *never* assume that either of these exist, since the call to the cache may always fail. // diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 86388f60cf4..acfd4a1e71f 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -85,6 +85,7 @@ type ExtRequestTargeting struct { IncludeWinners bool `json:"includewinners"` IncludeBidderKeys bool `json:"includebidderkeys"` IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory"` + IncludeFormat bool `json:"includeformat"` DurationRangeSec []int `json:"durationrangesec"` } From deb19c39420a9315a8838d52819cffd7479a48d6 Mon Sep 17 00:00:00 2001 From: gpolaert Date: Mon, 3 Aug 2020 19:38:59 +0200 Subject: [PATCH 155/318] feat: Add new logger module - Pubstack Analytics Module (#1331) * Pubstack Analytics V1 (#11) * V1 Pubstack (#7) * feat: Add Pubstack Logger (#6) * first version of pubstack analytics * bypass viperconfig * commit #1 * gofmt * update configuration and make the tests pass * add readme on how to configure the adapter and update the network calls * update logging and fix intake url definition * feat: Pubstack Analytics Connector * fixing go mod * fix: bad behaviour on appending path to auction url * add buffering * support bootstyrap like configuration * implement route for all the objects * supports termination signal handling for goroutines * move readme to the correct location * wording * enable configuration reload + add tests * fix logs messages * fix tests * fix log line * conclude merge * merge * update go mod Co-authored-by: Amaury Ravanel * fix duplicated channel keys Co-authored-by: Amaury Ravanel * first pass - PR reviews * rename channel* -> eventChannel * dead code * Review (#10) * use json.Decoder * update documentation * use nil instead []byte("") * clean code * do not use http.DefaultClient * fix race condition (need validation) * separate the sender and buffer logics * refactor the default configuration * remove error counter * Review GP + AR * updating default config * add more logs * remove alias fields in json * fix json serializer * close event channels Co-authored-by: Amaury Ravanel * fix race condition * first pass (pr reviews) * refactor: store enabled modules into a dedicated struct * stop goroutine * test: improve coverage * PR Review * Revert "refactor: store enabled modules into a dedicated struct" This reverts commit f57d9d61680c74244effc39a5d96d6cbb2f19f7d. # Conflicts: # analytics/config/config_test.go Co-authored-by: Amaury Ravanel --- analytics/clients/http.go | 12 + analytics/config/config.go | 17 ++ analytics/config/config_test.go | 41 +++ analytics/pubstack/README.md | 28 ++ analytics/pubstack/config.go | 51 ++++ analytics/pubstack/config_test.go | 102 +++++++ .../pubstack/eventchannel/eventchannel.go | 137 +++++++++ .../eventchannel/eventchannel_test.go | 136 +++++++++ analytics/pubstack/eventchannel/sender.go | 45 +++ .../pubstack/eventchannel/sender_test.go | 40 +++ analytics/pubstack/helpers/json.go | 88 ++++++ analytics/pubstack/helpers/json_test.go | 61 ++++ .../pubstack/mocks/mock_openrtb_request.json | 64 ++++ .../pubstack/mocks/mock_openrtb_response.json | 91 ++++++ analytics/pubstack/pubstack_module.go | 273 ++++++++++++++++++ analytics/pubstack/pubstack_module_test.go | 186 ++++++++++++ config/config.go | 24 +- go.mod | 1 + go.sum | 2 + 19 files changed, 1398 insertions(+), 1 deletion(-) create mode 100644 analytics/clients/http.go create mode 100644 analytics/pubstack/README.md create mode 100644 analytics/pubstack/config.go create mode 100644 analytics/pubstack/config_test.go create mode 100644 analytics/pubstack/eventchannel/eventchannel.go create mode 100644 analytics/pubstack/eventchannel/eventchannel_test.go create mode 100644 analytics/pubstack/eventchannel/sender.go create mode 100644 analytics/pubstack/eventchannel/sender_test.go create mode 100644 analytics/pubstack/helpers/json.go create mode 100644 analytics/pubstack/helpers/json_test.go create mode 100644 analytics/pubstack/mocks/mock_openrtb_request.json create mode 100644 analytics/pubstack/mocks/mock_openrtb_response.json create mode 100644 analytics/pubstack/pubstack_module.go create mode 100644 analytics/pubstack/pubstack_module_test.go diff --git a/analytics/clients/http.go b/analytics/clients/http.go new file mode 100644 index 00000000000..bc7f863ebdd --- /dev/null +++ b/analytics/clients/http.go @@ -0,0 +1,12 @@ +package clients + +import ( + "net/http" +) + +var defaultHttpInstance = http.DefaultClient + +func GetDefaultHttpInstance() *http.Client { + // TODO 2020-06-22 @see https://github.com/prebid/prebid-server/pull/1331#discussion_r436110097 + return defaultHttpInstance +} diff --git a/analytics/config/config.go b/analytics/config/config.go index 7be7c8ecca3..7f7ded0ffc4 100644 --- a/analytics/config/config.go +++ b/analytics/config/config.go @@ -3,7 +3,9 @@ package config import ( "github.com/golang/glog" "github.com/prebid/prebid-server/analytics" + "github.com/prebid/prebid-server/analytics/clients" "github.com/prebid/prebid-server/analytics/filesystem" + "github.com/prebid/prebid-server/analytics/pubstack" "github.com/prebid/prebid-server/config" ) @@ -17,6 +19,21 @@ func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule { glog.Fatalf("Could not initialize FileLogger for file %v :%v", analytics.File.Filename, err) } } + if analytics.Pubstack.Enabled { + pubstackModule, err := pubstack.NewPubstackModule( + clients.GetDefaultHttpInstance(), + analytics.Pubstack.ScopeId, + analytics.Pubstack.IntakeUrl, + analytics.Pubstack.ConfRefresh, + analytics.Pubstack.Buffers.EventCount, + analytics.Pubstack.Buffers.BufferSize, + analytics.Pubstack.Buffers.Timeout) + if err == nil { + modules = append(modules, pubstackModule) + } else { + glog.Errorf("Could not initialize PubstackModule: %v", err) + } + } return modules } diff --git a/analytics/config/config_test.go b/analytics/config/config_test.go index 7d97fb5f1be..583d475e786 100644 --- a/analytics/config/config_test.go +++ b/analytics/config/config_test.go @@ -1,6 +1,7 @@ package config import ( + "github.com/stretchr/testify/assert" "net/http" "os" "testing" @@ -73,6 +74,13 @@ func initAnalytics(count *int) analytics.PBSAnalyticsModule { } func TestNewPBSAnalytics(t *testing.T) { + pbsAnalytics := NewPBSAnalytics(&config.Analytics{}) + instance := pbsAnalytics.(enabledAnalytics) + + assert.Equal(t, len(instance), 0) +} + +func TestNewPBSAnalytics_FileLogger(t *testing.T) { if _, err := os.Stat(TEST_DIR); os.IsNotExist(err) { if err = os.MkdirAll(TEST_DIR, 0755); err != nil { t.Fatalf("Could not create test directory for FileLogger") @@ -88,4 +96,37 @@ func TestNewPBSAnalytics(t *testing.T) { default: t.Fatalf("Failed to initialize analytics module") } + + pbsAnalytics := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}}) + instance := pbsAnalytics.(enabledAnalytics) + + assert.Equal(t, len(instance), 1) +} + +func TestNewPBSAnalytics_Pubstack(t *testing.T) { + + pbsAnalyticsWithoutError := NewPBSAnalytics(&config.Analytics{ + Pubstack: config.Pubstack{ + Enabled: true, + ScopeId: "scopeId", + IntakeUrl: "https://pubstack.io/intake", + Buffers: config.PubstackBuffer{ + BufferSize: "100KB", + EventCount: 0, + Timeout: "30s", + }, + ConfRefresh: "2h", + }, + }) + instanceWithoutError := pbsAnalyticsWithoutError.(enabledAnalytics) + + assert.Equal(t, len(instanceWithoutError), 1) + + pbsAnalyticsWithError := NewPBSAnalytics(&config.Analytics{ + Pubstack: config.Pubstack{ + Enabled: true, + }, + }) + instanceWithError := pbsAnalyticsWithError.(enabledAnalytics) + assert.Equal(t, len(instanceWithError), 0) } diff --git a/analytics/pubstack/README.md b/analytics/pubstack/README.md new file mode 100644 index 00000000000..51c5fdb6bb3 --- /dev/null +++ b/analytics/pubstack/README.md @@ -0,0 +1,28 @@ +# Pubstack Analytics + +In order to use the pubstack analytics module, it needs to be configured by the host. + +You can configure the server using the following environment variables: + +```bash +export PBS_ANALYTICS_PUBSTACK_ENABLED="true" +export PBS_ANALYTICS_PUBSTACK_ENDPOINT="https://openrtb.preview.pubstack.io/v1/openrtb2" +export PBS_ANALYTICS_PUBSTACK_SCOPEID= # should be an UUIDv4 +``` + +Or using the pbs configuration file and by appending the following block: + +```yaml +analytics: + pubstack: + # Required properties + enabled: true + endpoint: "https://openrtb.preview.pubstack.io/v1/openrtb2" + scopeid: "" # The scopeId provided by the Pubstack Support Team + # Optional properties (advanced configuration) + configuration_refresh_delay: "2h" # Dynamic configuration delay + buffers: # Flush events to Pubstack when (first condition reached) + size: "2MB" # greater than 2MB + count : 100 # greater than 100 events + timeout: "15m" # greater than 15 minutes +``` \ No newline at end of file diff --git a/analytics/pubstack/config.go b/analytics/pubstack/config.go new file mode 100644 index 00000000000..472acf68ead --- /dev/null +++ b/analytics/pubstack/config.go @@ -0,0 +1,51 @@ +package pubstack + +import ( + "encoding/json" + "github.com/docker/go-units" + "net/http" + "net/url" + "time" +) + +func fetchConfig(client *http.Client, endpoint *url.URL) (*Configuration, error) { + + res, err := client.Get(endpoint.String()) + if err != nil { + return nil, err + } + + defer res.Body.Close() + c := Configuration{} + err = json.NewDecoder(res.Body).Decode(&c) + if err != nil { + return nil, err + } + return &c, nil +} + +func newBufferConfig(count int, size, duration string) (*bufferConfig, error) { + pDuration, err := time.ParseDuration(duration) + if err != nil { + return nil, err + } + pSize, err := units.FromHumanSize(size) + if err != nil { + return nil, err + } + return &bufferConfig{ + pDuration, + int64(count), + pSize, + }, nil +} + +func (a *Configuration) isSameAs(b *Configuration) bool { + sameEndpoint := a.Endpoint == b.Endpoint + sameScopeID := a.ScopeID == b.ScopeID + sameFeature := len(a.Features) == len(b.Features) + for key := range a.Features { + sameFeature = sameFeature && a.Features[key] == b.Features[key] + } + return sameFeature && sameEndpoint && sameScopeID +} diff --git a/analytics/pubstack/config_test.go b/analytics/pubstack/config_test.go new file mode 100644 index 00000000000..bb6fd0bddbb --- /dev/null +++ b/analytics/pubstack/config_test.go @@ -0,0 +1,102 @@ +package pubstack + +import ( + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestFetchConfig(t *testing.T) { + configResponse := `{ + "scopeId": "scopeId", + "endpoint": "https://pubstack.io", + "features": { + "auction": true, + "cookiesync": true, + "amp": true, + "setuid": false, + "video": false + } + }` + + server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + res.Write([]byte(configResponse)) + res.WriteHeader(200) + })) + + defer server.Close() + + endpoint, _ := url.Parse(server.URL) + cfg, _ := fetchConfig(server.Client(), endpoint) + + assert.Equal(t, cfg.ScopeID, "scopeId") + assert.Equal(t, cfg.Endpoint, "https://pubstack.io") + assert.Equal(t, cfg.Features[auction], true) + assert.Equal(t, cfg.Features[cookieSync], true) + assert.Equal(t, cfg.Features[amp], true) + assert.Equal(t, cfg.Features[setUID], false) + assert.Equal(t, cfg.Features[video], false) +} + +func TestFetchConfig_Error(t *testing.T) { + configResponse := `{ + "error": "scopeId", + }` + + server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + res.Write([]byte(configResponse)) + res.WriteHeader(200) + })) + + defer server.Close() + + endpoint, _ := url.Parse(server.URL) + cfg, err := fetchConfig(server.Client(), endpoint) + + assert.Nil(t, cfg) + assert.NotNil(t, err) +} + +func TestIsSameAs(t *testing.T) { + copyConfig := func(conf Configuration) *Configuration { + newConfig := conf + newConfig.Features = make(map[string]bool) + for k := range conf.Features { + newConfig.Features[k] = conf.Features[k] + } + return &newConfig + } + + a := &Configuration{ + ScopeID: "scopeId", + Endpoint: "endpoint", + Features: map[string]bool{ + "auction": true, + "cookiesync": true, + "amp": true, + "setuid": false, + "video": false, + }, + } + + assert.True(t, a.isSameAs(copyConfig(*a))) + + b := copyConfig(*a) + b.ScopeID = "anotherId" + assert.False(t, a.isSameAs(b)) + + b = copyConfig(*a) + b.Endpoint = "anotherEndpoint" + assert.False(t, a.isSameAs(b)) + + b = copyConfig(*a) + b.Features["auction"] = true + assert.True(t, a.isSameAs(b)) + b.Features["auction"] = false + assert.False(t, a.isSameAs(b)) + +} diff --git a/analytics/pubstack/eventchannel/eventchannel.go b/analytics/pubstack/eventchannel/eventchannel.go new file mode 100644 index 00000000000..b8dc4dd8e28 --- /dev/null +++ b/analytics/pubstack/eventchannel/eventchannel.go @@ -0,0 +1,137 @@ +package eventchannel + +import ( + "bytes" + "compress/gzip" + "sync" + "time" + + "github.com/golang/glog" +) + +type Metrics struct { + bufferSize int64 + eventCount int64 +} +type Limit struct { + maxByteSize int64 + maxEventCount int64 + maxTime time.Duration +} +type EventChannel struct { + gz *gzip.Writer + buff *bytes.Buffer + + ch chan []byte + endCh chan int + metrics Metrics + muxGzBuffer sync.RWMutex + send Sender + limit Limit +} + +func NewEventChannel(sender Sender, maxByteSize, maxEventCount int64, maxTime time.Duration) *EventChannel { + b := &bytes.Buffer{} + gzw := gzip.NewWriter(b) + + c := EventChannel{ + gz: gzw, + buff: b, + ch: make(chan []byte), + endCh: make(chan int), + metrics: Metrics{}, + send: sender, + limit: Limit{maxByteSize, maxEventCount, maxTime}, + } + go c.start() + return &c +} + +func (c *EventChannel) Push(event []byte) { + c.ch <- event +} + +func (c *EventChannel) Close() { + c.endCh <- 1 +} + +func (c *EventChannel) buffer(event []byte) { + c.muxGzBuffer.Lock() + defer c.muxGzBuffer.Unlock() + + _, err := c.gz.Write(event) + if err != nil { + glog.Warning("[pubstack] fail to compress, skip the event") + return + } + + c.metrics.eventCount++ + c.metrics.bufferSize += int64(len(event)) +} + +func (c *EventChannel) isBufferFull() bool { + c.muxGzBuffer.RLock() + defer c.muxGzBuffer.RUnlock() + return c.metrics.eventCount >= c.limit.maxEventCount || c.metrics.bufferSize >= c.limit.maxByteSize +} + +func (c *EventChannel) reset() { + // reset buffer + c.gz.Reset(c.buff) + c.buff.Reset() + + // reset metrics + c.metrics.eventCount = 0 + c.metrics.bufferSize = 0 +} + +func (c *EventChannel) flush() { + c.muxGzBuffer.Lock() + defer c.muxGzBuffer.Unlock() + + if c.metrics.eventCount == 0 || c.metrics.bufferSize == 0 { + return + } + + // finish writing gzip header + err := c.gz.Flush() + if err != nil { + glog.Warning("[pubstack] fail to flush gzipped buffer") + return + } + + // copy the current buffer to send the payload in a new thread + payload := make([]byte, c.buff.Len()) + _, err = c.buff.Read(payload) + if err != nil { + glog.Warning("[pubstack] fail to copy the buffer") + return + } + + // reset buffers and writers + c.reset() + + // send events (async) + go c.send(payload) +} + +func (c *EventChannel) start() { + ticker := time.NewTicker(c.limit.maxTime) + + for { + select { + case <-c.endCh: + c.flush() + return + // event is received + case event := <-c.ch: + c.buffer(event) + if c.isBufferFull() { + c.flush() + } + // time between 2 flushes has passed + case <-ticker.C: + c.flush() + } + } +} diff --git a/analytics/pubstack/eventchannel/eventchannel_test.go b/analytics/pubstack/eventchannel/eventchannel_test.go new file mode 100644 index 00000000000..9fdcfe976a6 --- /dev/null +++ b/analytics/pubstack/eventchannel/eventchannel_test.go @@ -0,0 +1,136 @@ +package eventchannel + +import ( + "bytes" + "compress/gzip" + "github.com/stretchr/testify/assert" + "io/ioutil" + "sync" + "testing" + "time" +) + +var maxByteSize = int64(15) +var maxEventCount = int64(3) +var maxTime = 2 * time.Hour + +func readGz(encoded bytes.Buffer) string { + gr, _ := gzip.NewReader(bytes.NewBuffer(encoded.Bytes())) + defer gr.Close() + + decoded, _ := ioutil.ReadAll(gr) + return string(decoded) +} + +func newSender(data *[]byte) Sender { + mux := &sync.Mutex{} + return func(payload []byte) error { + mux.Lock() + defer mux.Unlock() + event := bytes.Buffer{} + event.Write(payload) + *data = append(*data, readGz(event)...) + return nil + } +} + +func TestEventChannel_isBufferFull(t *testing.T) { + + send := func(_ []byte) error { return nil } + + eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime) + defer eventChannel.Close() + + eventChannel.buffer([]byte("one")) + eventChannel.buffer([]byte("two")) + + assert.Equal(t, eventChannel.isBufferFull(), false) + + eventChannel.buffer([]byte("three")) + + assert.Equal(t, eventChannel.isBufferFull(), true) + + eventChannel.reset() + + assert.Equal(t, eventChannel.isBufferFull(), false) + + eventChannel.buffer([]byte("big-event-abcdefghijklmnopqrstuvwxyz")) + + assert.Equal(t, eventChannel.isBufferFull(), true) + +} + +func TestEventChannel_reset(t *testing.T) { + send := func(_ []byte) error { return nil } + + eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime) + defer eventChannel.Close() + + assert.Equal(t, eventChannel.metrics.eventCount, int64(0)) + assert.Equal(t, eventChannel.metrics.bufferSize, int64(0)) + + eventChannel.buffer([]byte("one")) + eventChannel.buffer([]byte("two")) + + assert.NotEqual(t, eventChannel.metrics.eventCount, int64(0)) + assert.NotEqual(t, eventChannel.metrics.bufferSize, int64(0)) + + eventChannel.reset() + + assert.Equal(t, eventChannel.buff.Len(), 0) + assert.Equal(t, eventChannel.metrics.eventCount, int64(0)) + assert.Equal(t, eventChannel.metrics.bufferSize, int64(0)) +} + +func TestEventChannel_flush(t *testing.T) { + data := make([]byte, 0) + send := newSender(&data) + + eventChannel := NewEventChannel(send, maxByteSize, maxEventCount, maxTime) + defer eventChannel.Close() + + eventChannel.buffer([]byte("one")) + eventChannel.buffer([]byte("two")) + eventChannel.buffer([]byte("three")) + eventChannel.flush() + time.Sleep(10 * time.Millisecond) + + assert.Equal(t, string(data), "onetwothree") +} + +func TestEventChannel_close(t *testing.T) { + data := make([]byte, 0) + send := newSender(&data) + + eventChannel := NewEventChannel(send, 15000, 15000, 2*time.Hour) + + eventChannel.buffer([]byte("one")) + eventChannel.buffer([]byte("two")) + eventChannel.buffer([]byte("three")) + eventChannel.Close() + + time.Sleep(10 * time.Millisecond) + + assert.Equal(t, string(data), "onetwothree") +} + +func TestEventChannel_Push(t *testing.T) { + data := make([]byte, 0) + send := newSender(&data) + + eventChannel := NewEventChannel(send, 15000, 5, 5*time.Millisecond) + defer eventChannel.Close() + + eventChannel.Push([]byte("one")) + eventChannel.Push([]byte("two")) + eventChannel.Push([]byte("three")) + eventChannel.Push([]byte("four")) + eventChannel.Push([]byte("five")) + eventChannel.Push([]byte("six")) + eventChannel.Push([]byte("seven")) + + time.Sleep(10 * time.Millisecond) + + assert.Equal(t, string(data), "onetwothreefourfivesixseven") + +} diff --git a/analytics/pubstack/eventchannel/sender.go b/analytics/pubstack/eventchannel/sender.go new file mode 100644 index 00000000000..951de4d414e --- /dev/null +++ b/analytics/pubstack/eventchannel/sender.go @@ -0,0 +1,45 @@ +package eventchannel + +import ( + "bytes" + "fmt" + "github.com/golang/glog" + "net/http" + "net/url" + "path" +) + +type Sender = func(payload []byte) error + +func NewHttpSender(client *http.Client, endpoint string) Sender { + return func(payload []byte) error { + req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(payload)) + if err != nil { + glog.Error(err) + return err + } + + req.Header.Set("Content-Type", "application/octet-stream") + req.Header.Set("Content-Encoding", "gzip") + + resp, err := client.Do(req) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + glog.Errorf("[pubstack] Wrong code received %d instead of %d", resp.StatusCode, http.StatusOK) + return fmt.Errorf("wrong code received %d instead of %d", resp.StatusCode, http.StatusOK) + } + return nil + } +} + +func BuildEndpointSender(client *http.Client, baseUrl string, module string) Sender { + endpoint, err := url.Parse(baseUrl) + if err != nil { + glog.Error(err) + } + endpoint.Path = path.Join(endpoint.Path, "intake", module) + return NewHttpSender(client, endpoint.String()) +} diff --git a/analytics/pubstack/eventchannel/sender_test.go b/analytics/pubstack/eventchannel/sender_test.go new file mode 100644 index 00000000000..1185435e4ab --- /dev/null +++ b/analytics/pubstack/eventchannel/sender_test.go @@ -0,0 +1,40 @@ +package eventchannel + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuildEndpointSender(t *testing.T) { + requestBody := make([]byte, 10) + server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + requestBody, _ = ioutil.ReadAll(req.Body) + res.WriteHeader(200) + })) + + defer server.Close() + + sender := BuildEndpointSender(server.Client(), server.URL, "module") + err := sender([]byte("message")) + + assert.Equal(t, requestBody, []byte("message")) + assert.Nil(t, err) +} + +func TestBuildEndpointSender_Error(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(400) + })) + + defer server.Close() + + sender := BuildEndpointSender(server.Client(), server.URL, "module") + err := sender([]byte("message")) + + assert.NotNil(t, err) +} diff --git a/analytics/pubstack/helpers/json.go b/analytics/pubstack/helpers/json.go new file mode 100644 index 00000000000..f02f1120626 --- /dev/null +++ b/analytics/pubstack/helpers/json.go @@ -0,0 +1,88 @@ +package helpers + +import ( + "encoding/json" + "fmt" + + "github.com/prebid/prebid-server/analytics" +) + +func JsonifyAuctionObject(ao *analytics.AuctionObject, scope string) ([]byte, error) { + b, err := json.Marshal(&struct { + Scope string `json:"scope"` + *analytics.AuctionObject + }{ + Scope: scope, + AuctionObject: ao, + }) + + if err == nil { + b = append(b, byte('\n')) + return b, nil + } + return nil, fmt.Errorf("auction object badly formed %v", err) +} + +func JsonifyVideoObject(vo *analytics.VideoObject, scope string) ([]byte, error) { + b, err := json.Marshal(&struct { + Scope string `json:"scope"` + *analytics.VideoObject + }{ + Scope: scope, + VideoObject: vo, + }) + + if err == nil { + b = append(b, byte('\n')) + return b, nil + } + return nil, fmt.Errorf("video object badly formed %v", err) +} + +func JsonifyCookieSync(cso *analytics.CookieSyncObject, scope string) ([]byte, error) { + b, err := json.Marshal(&struct { + Scope string `json:"scope"` + *analytics.CookieSyncObject + }{ + Scope: scope, + CookieSyncObject: cso, + }) + + if err == nil { + b = append(b, byte('\n')) + return b, nil + } + return nil, fmt.Errorf("cookie sync object badly formed %v", err) +} + +func JsonifySetUIDObject(so *analytics.SetUIDObject, scope string) ([]byte, error) { + b, err := json.Marshal(&struct { + Scope string `json:"scope"` + *analytics.SetUIDObject + }{ + Scope: scope, + SetUIDObject: so, + }) + + if err == nil { + b = append(b, byte('\n')) + return b, nil + } + return nil, fmt.Errorf("set UID object badly formed %v", err) +} + +func JsonifyAmpObject(ao *analytics.AmpObject, scope string) ([]byte, error) { + b, err := json.Marshal(&struct { + Scope string `json:"scope"` + *analytics.AmpObject + }{ + Scope: scope, + AmpObject: ao, + }) + + if err == nil { + b = append(b, byte('\n')) + return b, nil + } + return nil, fmt.Errorf("amp object badly formed %v", err) +} diff --git a/analytics/pubstack/helpers/json_test.go b/analytics/pubstack/helpers/json_test.go new file mode 100644 index 00000000000..4e36e8db2be --- /dev/null +++ b/analytics/pubstack/helpers/json_test.go @@ -0,0 +1,61 @@ +package helpers + +import ( + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/analytics" + "github.com/prebid/prebid-server/usersync" + "net/http" + "testing" +) + +func TestJsonifyAuctionObject(t *testing.T) { + ao := &analytics.AuctionObject{ + Status: http.StatusOK, + } + if _, err := JsonifyAuctionObject(ao, "scopeId"); err != nil { + t.Fail() + } + +} + +func TestJsonifyVideoObject(t *testing.T) { + vo := &analytics.VideoObject{ + Status: http.StatusOK, + } + if _, err := JsonifyVideoObject(vo, "scopeId"); err != nil { + t.Fail() + } +} + +func TestJsonifyCookieSync(t *testing.T) { + cso := &analytics.CookieSyncObject{ + Status: http.StatusOK, + BidderStatus: []*usersync.CookieSyncBidders{}, + } + if _, err := JsonifyCookieSync(cso, "scopeId"); err != nil { + t.Fail() + } +} + +func TestJsonifySetUIDObject(t *testing.T) { + so := &analytics.SetUIDObject{ + Status: http.StatusOK, + Bidder: "any-bidder", + UID: "uid string", + } + if _, err := JsonifySetUIDObject(so, "scopeId"); err != nil { + t.Fail() + } +} + +func TestJsonifyAmpObject(t *testing.T) { + ao := &analytics.AmpObject{ + Status: http.StatusOK, + Errors: make([]error, 0), + AuctionResponse: &openrtb.BidResponse{}, + AmpTargetingValues: map[string]string{}, + } + if _, err := JsonifyAmpObject(ao, "scopeId"); err != nil { + t.Fail() + } +} diff --git a/analytics/pubstack/mocks/mock_openrtb_request.json b/analytics/pubstack/mocks/mock_openrtb_request.json new file mode 100644 index 00000000000..03b9665b247 --- /dev/null +++ b/analytics/pubstack/mocks/mock_openrtb_request.json @@ -0,0 +1,64 @@ +{ + "id": "19c2eeb8-824c-4604-af41-a59b2b7bb895", + "site": { + "page": "https%3A%2F%2Fdebug.mediasquare.fr%2Fdebug%2Fprebid%2Fmsq_desktop.html%3Fpbjs_debug%3Dtrue" + }, + "user": { + "ext": {} + }, + "regs": { + "ext": {} + }, + "test": 1, + "imp": [ + { + "id": "0341252e-b3b0-4dff-a0ef-1ced63369bd5", + "ext": { + "appnexus": { + "placementId": 5724999 + } + }, + "secure": 1, + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + } + }, + { + "id": "3ac0ffa3-01de-44d2-9baf-1fee79026624", + "ext": { + "msqClassic": { + "placementId": 10471298 + } + }, + "secure": 1, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "bidadjustmentfactors": { + "msqClassic": 0.8, + "msqBrand": 0.8, + "msqMax": 0.8, + "msqMaxView": 0.8 + } + } + } +} \ No newline at end of file diff --git a/analytics/pubstack/mocks/mock_openrtb_response.json b/analytics/pubstack/mocks/mock_openrtb_response.json new file mode 100644 index 00000000000..6f4d1965b8c --- /dev/null +++ b/analytics/pubstack/mocks/mock_openrtb_response.json @@ -0,0 +1,91 @@ +{ + "id": "19c2eeb8-824c-4604-af41-a59b2b7bb895", + "seatbid": [{ + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "0341252e-b3b0-4dff-a0ef-1ced63369bd5", + "price": 0.500000, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": ["appnexus.com"], + "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110", + "cid": "958", + "crid": "29681110", + "h": 970, + "w": 250, + "ext": { + "appnexus": { + "brand_id": 1, + "brand_category_id": 1, + "auction_id": 8189378542222915032, + "bid_ad_type": 0, + "bidder_id": 2, + "ranking_price": 0.000000 + } + } + }, + { + "id": "7706636740145184842", + "impid": "0341252e-b3b0-4dff-a0ef-1ced63369bd5", + "price": 0.0, + "adid": "29681114", + "adm": "some-test-ad2", + "adomain": ["appnexus.com"], + "iurl": "http://nym1-ib.adnxs.com/cr?id=29681114", + "cid": "959", + "crid": "29681114", + "h": 970, + "w": 250, + "ext": { + "appnexus": { + "brand_id": 1, + "brand_category_id": 1, + "auction_id": 8189378542222915032, + "bid_ad_type": 0, + "bidder_id": 2, + "ranking_price": 0.000000 + } + } + },{ + "id": "7706636740145184842", + "impid": "3ac0ffa3-01de-44d2-9baf-1fee79026624", + "price": 0.5234, + "adid": "29681113", + "adm": "some-test-ad2", + "adomain": ["appnexus.com"], + "iurl": "http://nym1-ib.adnxs.com/cr?id=29681113", + "cid": "959", + "crid": "29681113", + "h": 970, + "w": 250, + "ext": { + "appnexus": { + "brand_id": 1, + "brand_category_id": 1, + "auction_id": 8189378542222915032, + "bid_ad_type": 0, + "bidder_id": 2, + "ranking_price": 0.000000 + } + } + }] + }, { + "seat": "improvedigital", + "bid": [{ + "id": "randomid", + "impid": "0341252e-b3b0-4dff-a0ef-1ced63369bd5", + "price": 0.510000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" +} diff --git a/analytics/pubstack/pubstack_module.go b/analytics/pubstack/pubstack_module.go new file mode 100644 index 00000000000..9f1a81c7232 --- /dev/null +++ b/analytics/pubstack/pubstack_module.go @@ -0,0 +1,273 @@ +package pubstack + +import ( + "fmt" + "github.com/prebid/prebid-server/analytics/pubstack/eventchannel" + "net/http" + "net/url" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/golang/glog" + "github.com/prebid/prebid-server/analytics/pubstack/helpers" + + "github.com/prebid/prebid-server/analytics" +) + +type Configuration struct { + ScopeID string `json:"scopeId"` + Endpoint string `json:"endpoint"` + Features map[string]bool `json:"features"` +} + +// routes for events +const ( + auction = "auction" + cookieSync = "cookiesync" + amp = "amp" + setUID = "setuid" + video = "video" +) + +type bufferConfig struct { + timeout time.Duration + count int64 + size int64 +} + +type PubstackModule struct { + eventChannels map[string]*eventchannel.EventChannel + httpClient *http.Client + configCh chan *Configuration + sigTermCh chan os.Signal + scope string + cfg *Configuration + buffsCfg *bufferConfig + muxConfig sync.RWMutex +} + +func NewPubstackModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string) (analytics.PBSAnalyticsModule, error) { + glog.Infof("[pubstack] Initializing module scope=%s endpoint=%s\n", scope, endpoint) + + // parse args + + refreshDelay, err := time.ParseDuration(configRefreshDelay) + if err != nil { + return nil, fmt.Errorf("fail to parse the module args, arg=analytics.pubstack.configuration_refresh_delay, :%v", err) + } + + bufferCfg, err := newBufferConfig(maxEventCount, maxByteSize, maxTime) + if err != nil { + return nil, fmt.Errorf("fail to parse the module args, arg=analytics.pubstack.buffers, :%v", err) + } + + defaultFeatures := map[string]bool{ + auction: false, + video: false, + amp: false, + cookieSync: false, + setUID: false, + } + + defaultConfig := &Configuration{ + ScopeID: scope, + Endpoint: endpoint, + Features: defaultFeatures, + } + + pb := PubstackModule{ + scope: scope, + httpClient: client, + cfg: defaultConfig, + buffsCfg: bufferCfg, + sigTermCh: make(chan os.Signal), + configCh: make(chan *Configuration), + eventChannels: make(map[string]*eventchannel.EventChannel), + muxConfig: sync.RWMutex{}, + } + signal.Notify(pb.sigTermCh, os.Interrupt, syscall.SIGTERM) + + configUrl, err := url.Parse(pb.cfg.Endpoint + "/bootstrap?scopeId=" + pb.cfg.ScopeID) + if err != nil { + glog.Error(err) + return nil, err + } + go pb.start(configUrl, refreshDelay) + go func() { + err = pb.reloadConfig(configUrl) + if err != nil { + glog.Errorf("[pubstack] Fail to fetch remote configuration: %v", err) + } + }() + + glog.Info("[pubstack] Pubstack analytics configured and ready") + return &pb, nil +} + +func (p *PubstackModule) LogAuctionObject(ao *analytics.AuctionObject) { + p.muxConfig.RLock() + defer p.muxConfig.RUnlock() + + if !p.isFeatureEnable(auction) { + return + } + + // serialize event + payload, err := helpers.JsonifyAuctionObject(ao, p.scope) + if err != nil { + glog.Warning("[pubstack] Cannot serialize auction") + return + } + + p.eventChannels[auction].Push(payload) +} + +func (p *PubstackModule) LogVideoObject(vo *analytics.VideoObject) { + p.muxConfig.RLock() + defer p.muxConfig.RUnlock() + + if !p.isFeatureEnable(video) { + return + } + + // serialize event + payload, err := helpers.JsonifyVideoObject(vo, p.scope) + if err != nil { + glog.Warning("[pubstack] Cannot serialize video") + return + } + + p.eventChannels[video].Push(payload) +} + +func (p *PubstackModule) LogSetUIDObject(so *analytics.SetUIDObject) { + p.muxConfig.RLock() + defer p.muxConfig.RUnlock() + + if !p.isFeatureEnable(setUID) { + return + } + + // serialize event + payload, err := helpers.JsonifySetUIDObject(so, p.scope) + if err != nil { + glog.Warning("[pubstack] Cannot serialize video") + return + } + + p.eventChannels[setUID].Push(payload) +} + +func (p *PubstackModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { + p.muxConfig.RLock() + defer p.muxConfig.RUnlock() + + if !p.isFeatureEnable(cookieSync) { + return + } + + // serialize event + payload, err := helpers.JsonifyCookieSync(cso, p.scope) + if err != nil { + glog.Warning("[pubstack] Cannot serialize video") + return + } + + p.eventChannels[cookieSync].Push(payload) + +} + +func (p *PubstackModule) LogAmpObject(ao *analytics.AmpObject) { + p.muxConfig.RLock() + defer p.muxConfig.RUnlock() + + if !p.isFeatureEnable(amp) { + return + } + + // serialize event + payload, err := helpers.JsonifyAmpObject(ao, p.scope) + if err != nil { + glog.Warning("[pubstack] Cannot serialize video") + return + } + + p.eventChannels[amp].Push(payload) + +} + +func (p *PubstackModule) reloadConfig(configUrl *url.URL) error { + config, err := fetchConfig(p.httpClient, configUrl) + if err != nil { + return err + } + p.configCh <- config + return nil +} + +func (p *PubstackModule) start(configUrl *url.URL, refreshDelay time.Duration) { + + tick := time.NewTicker(refreshDelay) + + for { + select { + case <-p.sigTermCh: + p.closeAllEventChannels() + return + case config := <-p.configCh: + p.updateConfig(config) + glog.Infof("[pubstack] Updating config: %v", p.cfg) + case <-tick.C: + go func() { + err := p.reloadConfig(configUrl) + if err != nil { + glog.Errorf("[pubstack] Fail to fetch remote configuration: %v", err) + } + }() + } + } + +} + +func (p *PubstackModule) updateConfig(config *Configuration) { + p.muxConfig.Lock() + defer p.muxConfig.Unlock() + + if p.cfg.isSameAs(config) { + return + } + + p.cfg = config + p.closeAllEventChannels() + + if p.isFeatureEnable(amp) { + p.eventChannels[amp] = eventchannel.NewEventChannel(eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, amp), p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout) + } + if p.isFeatureEnable(auction) { + p.eventChannels[auction] = eventchannel.NewEventChannel(eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, auction), p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout) + } + if p.isFeatureEnable(cookieSync) { + p.eventChannels[cookieSync] = eventchannel.NewEventChannel(eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, cookieSync), p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout) + } + if p.isFeatureEnable(video) { + p.eventChannels[video] = eventchannel.NewEventChannel(eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, video), p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout) + } + if p.isFeatureEnable(setUID) { + p.eventChannels[setUID] = eventchannel.NewEventChannel(eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, setUID), p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout) + } +} + +func (p *PubstackModule) closeAllEventChannels() { + for key, ch := range p.eventChannels { + ch.Close() + delete(p.eventChannels, key) + } +} + +func (p *PubstackModule) isFeatureEnable(feature string) bool { + val, ok := p.cfg.Features[feature] + return ok && val +} diff --git a/analytics/pubstack/pubstack_module_test.go b/analytics/pubstack/pubstack_module_test.go new file mode 100644 index 00000000000..8d4dfdd689f --- /dev/null +++ b/analytics/pubstack/pubstack_module_test.go @@ -0,0 +1,186 @@ +package pubstack + +import ( + "encoding/json" + "github.com/prebid/prebid-server/analytics/pubstack/eventchannel" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + "time" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/analytics" + "github.com/stretchr/testify/assert" +) + +func loadJsonFromFile() (*analytics.AuctionObject, error) { + req, err := os.Open("mocks/mock_openrtb_request.json") + if err != nil { + return nil, err + } + defer req.Close() + + reqCtn := openrtb.BidRequest{} + reqPayload, err := ioutil.ReadAll(req) + if err != nil { + return nil, err + } + + err = json.Unmarshal(reqPayload, &reqCtn) + if err != nil { + return nil, err + } + + res, err := os.Open("mocks/mock_openrtb_response.json") + if err != nil { + return nil, err + } + defer res.Close() + + resCtn := openrtb.BidResponse{} + resPayload, err := ioutil.ReadAll(res) + if err != nil { + return nil, err + } + + err = json.Unmarshal(resPayload, &resCtn) + if err != nil { + return nil, err + } + + return &analytics.AuctionObject{ + Request: &reqCtn, + Response: &resCtn, + }, nil +} + +func TestPubstackModule(t *testing.T) { + + remoteConfig := &Configuration{} + server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + data, _ := json.Marshal(remoteConfig) + res.Write(data) + })) + client := server.Client() + + defer server.Close() + + // Loading Issues + _, err := NewPubstackModule(client, "scope", server.URL, "1z", 100, "90MB", "15m") + assert.NotNil(t, err) // should raise an error since we can't parse args // configRefreshDelay + + _, err = NewPubstackModule(client, "scope", server.URL, "1h", 100, "90z", "15m") + assert.NotNil(t, err) // should raise an error since we can't parse args // maxByte + + _, err = NewPubstackModule(client, "scope", server.URL, "1h", 100, "90MB", "15z") + assert.NotNil(t, err) // should raise an error since we can't parse args // maxTime + + // Loading OK + module, err := NewPubstackModule(client, "scope", server.URL, "10ms", 100, "90MB", "15m") + assert.Nil(t, err) + + // Default Configuration + pubstack, ok := module.(*PubstackModule) + assert.Equal(t, ok, true) //PBSAnalyticsModule is also a PubstackModule + assert.Equal(t, len(pubstack.cfg.Features), 5) + assert.Equal(t, pubstack.cfg.Features[auction], false) + assert.Equal(t, pubstack.cfg.Features[video], false) + assert.Equal(t, pubstack.cfg.Features[amp], false) + assert.Equal(t, pubstack.cfg.Features[setUID], false) + assert.Equal(t, pubstack.cfg.Features[cookieSync], false) + + assert.Equal(t, len(pubstack.eventChannels), 0) + + // Process Auction Event + counter := 0 + send := func(_ []byte) error { + counter++ + return nil + } + mockedEvent, err := loadJsonFromFile() + if err != nil { + t.Fail() + } + + pubstack.eventChannels[auction] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[video] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[amp] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[setUID] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[cookieSync] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + + pubstack.LogAuctionObject(mockedEvent) + pubstack.LogAmpObject(&analytics.AmpObject{ + Status: http.StatusOK, + }) + pubstack.LogCookieSyncObject(&analytics.CookieSyncObject{ + Status: http.StatusOK, + }) + pubstack.LogVideoObject(&analytics.VideoObject{ + Status: http.StatusOK, + }) + pubstack.LogSetUIDObject(&analytics.SetUIDObject{ + Status: http.StatusOK, + }) + + pubstack.closeAllEventChannels() + time.Sleep(10 * time.Millisecond) // process channel + assert.Equal(t, counter, 0) + + // Hot-Reload config + newFeatures := make(map[string]bool) + newFeatures[auction] = true + newFeatures[video] = true + newFeatures[amp] = true + newFeatures[cookieSync] = true + newFeatures[setUID] = true + + remoteConfig = &Configuration{ + ScopeID: "new-scope", + Endpoint: "new-endpoint", + Features: newFeatures, + } + + endpoint, _ := url.Parse(server.URL) + pubstack.reloadConfig(endpoint) + + time.Sleep(2 * time.Millisecond) // process channel + assert.Equal(t, len(pubstack.cfg.Features), 5) + assert.Equal(t, pubstack.cfg.Features[auction], true) + assert.Equal(t, pubstack.cfg.Features[video], true) + assert.Equal(t, pubstack.cfg.Features[amp], true) + assert.Equal(t, pubstack.cfg.Features[setUID], true) + assert.Equal(t, pubstack.cfg.Features[cookieSync], true) + assert.Equal(t, pubstack.cfg.ScopeID, "new-scope") + assert.Equal(t, pubstack.cfg.Endpoint, "new-endpoint") + assert.Equal(t, len(pubstack.eventChannels), 5) + + counter = 0 + pubstack.eventChannels[auction] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[video] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[amp] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[setUID] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + pubstack.eventChannels[cookieSync] = eventchannel.NewEventChannel(send, 2000, 1, 10*time.Second) + + pubstack.LogAuctionObject(mockedEvent) + pubstack.LogAmpObject(&analytics.AmpObject{ + Status: http.StatusOK, + }) + pubstack.LogCookieSyncObject(&analytics.CookieSyncObject{ + Status: http.StatusOK, + }) + pubstack.LogVideoObject(&analytics.VideoObject{ + Status: http.StatusOK, + }) + pubstack.LogSetUIDObject(&analytics.SetUIDObject{ + Status: http.StatusOK, + }) + pubstack.closeAllEventChannels() + time.Sleep(10 * time.Millisecond) + + assert.Equal(t, counter, 5) + +} diff --git a/config/config.go b/config/config.go index 8545523d238..67689d1ab1a 100755 --- a/config/config.go +++ b/config/config.go @@ -209,7 +209,8 @@ type LMT struct { } type Analytics struct { - File FileLogs `mapstructure:"file"` + File FileLogs `mapstructure:"file"` + Pubstack Pubstack `mapstructure:"pubstack"` } type CurrencyConverter struct { @@ -230,6 +231,20 @@ type FileLogs struct { Filename string `mapstructure:"filename"` } +type Pubstack struct { + Enabled bool `mapstructure:"enabled"` + ScopeId string `mapstructure:"scopeid"` + IntakeUrl string `mapstructure:"endpoint"` + Buffers PubstackBuffer `mapstructure:"buffers"` + ConfRefresh string `mapstructure:"configuration_refresh_delay"` +} + +type PubstackBuffer struct { + BufferSize string `mapstructure:"size"` + EventCount int `mapstructure:"count"` + Timeout string `mapstructure:"timeout"` +} + type HostCookie struct { Domain string `mapstructure:"domain"` Family string `mapstructure:"family"` @@ -855,6 +870,13 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("max_request_size", 1024*256) v.SetDefault("analytics.file.filename", "") + v.SetDefault("analytics.pubstack.endpoint", "https://s2s.pbstck.com/v1") + v.SetDefault("analytics.pubstack.scopeid", "change-me") + v.SetDefault("analytics.pubstack.enabled", false) + v.SetDefault("analytics.pubstack.configuration_refresh_delay", "2h") + v.SetDefault("analytics.pubstack.buffers.size", "2MB") + v.SetDefault("analytics.pubstack.buffers.count", 100) + v.SetDefault("analytics.pubstack.buffers.timeout", "900s") v.SetDefault("amp_timeout_adjustment_ms", 0) v.SetDefault("gdpr.host_vendor_id", 0) v.SetDefault("gdpr.usersync_if_ambiguous", false) diff --git a/go.mod b/go.mod index 00cadd31ce1..a5b5a161cf4 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/cespare/xxhash v1.0.0 // indirect github.com/chasex/glog v0.0.0-20160217080310-c62392af379c github.com/coocood/freecache v1.0.1 + github.com/docker/go-units v0.4.0 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd github.com/gofrs/uuid v3.2.0+incompatible diff --git a/go.sum b/go.sum index 5eaf37cad9f..1ddab71332a 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/coocood/freecache v1.0.1/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsip github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v0.0.0-20180720181644-f195058310bd h1:biTJQdqouE5by89AAffXG8++TY+9Fsdrg5rinbt3tHk= From 8740179c573f966147a91ce5f32b060bcc3e8243 Mon Sep 17 00:00:00 2001 From: Vikram Date: Thu, 6 Aug 2020 17:12:08 +0200 Subject: [PATCH 156/318] New bid adapter for Smaato (#1413) Co-authored-by: vikram Co-authored-by: Stephan --- adapters/smaato/image.go | 53 ++++ adapters/smaato/image_test.go | 44 +++ adapters/smaato/params_test.go | 65 +++++ adapters/smaato/richmedia.go | 52 ++++ adapters/smaato/richmedia_test.go | 39 +++ adapters/smaato/smaato.go | 276 ++++++++++++++++++ adapters/smaato/smaato_test.go | 11 + .../exemplary/simple-banner-richMedia.json | 194 ++++++++++++ .../smaatotest/exemplary/simple-banner.json | 190 ++++++++++++ adapters/smaato/smaatotest/params/banner.json | 4 + .../supplemental/bad-adm-response.json | 166 +++++++++++ .../smaatotest/supplemental/bad-ext-req.json | 54 ++++ .../bad-imp-banner-format-req.json | 61 ++++ .../supplemental/bad-user-ext-data-req.json | 67 +++++ .../supplemental/bad-user-ext-req.json | 57 ++++ .../supplemental/no-consent-info.json | 137 +++++++++ .../smaatotest/supplemental/no-imp-req.json | 17 ++ config/config.go | 1 + docs/bidders/smaato.md | 42 +++ exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_smaato.go | 9 + static/bidder-info/smaato.yaml | 9 + static/bidder-params/smaato.json | 17 ++ usersync/usersyncers/syncer_test.go | 1 + 25 files changed, 1570 insertions(+) create mode 100644 adapters/smaato/image.go create mode 100644 adapters/smaato/image_test.go create mode 100644 adapters/smaato/params_test.go create mode 100644 adapters/smaato/richmedia.go create mode 100644 adapters/smaato/richmedia_test.go create mode 100644 adapters/smaato/smaato.go create mode 100644 adapters/smaato/smaato_test.go create mode 100644 adapters/smaato/smaatotest/exemplary/simple-banner-richMedia.json create mode 100644 adapters/smaato/smaatotest/exemplary/simple-banner.json create mode 100644 adapters/smaato/smaatotest/params/banner.json create mode 100644 adapters/smaato/smaatotest/supplemental/bad-adm-response.json create mode 100644 adapters/smaato/smaatotest/supplemental/bad-ext-req.json create mode 100644 adapters/smaato/smaatotest/supplemental/bad-imp-banner-format-req.json create mode 100644 adapters/smaato/smaatotest/supplemental/bad-user-ext-data-req.json create mode 100644 adapters/smaato/smaatotest/supplemental/bad-user-ext-req.json create mode 100644 adapters/smaato/smaatotest/supplemental/no-consent-info.json create mode 100644 adapters/smaato/smaatotest/supplemental/no-imp-req.json create mode 100644 docs/bidders/smaato.md create mode 100644 openrtb_ext/imp_smaato.go create mode 100644 static/bidder-info/smaato.yaml create mode 100644 static/bidder-params/smaato.json diff --git a/adapters/smaato/image.go b/adapters/smaato/image.go new file mode 100644 index 00000000000..582206ccb0c --- /dev/null +++ b/adapters/smaato/image.go @@ -0,0 +1,53 @@ +package smaato + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" +) + +type imageAd struct { + Image image `json:"image"` +} +type image struct { + Img img `json:"img"` + Impressiontrackers []string `json:"impressiontrackers"` + Clicktrackers []string `json:"clicktrackers"` +} +type img struct { + URL string `json:"url"` + W int `json:"w"` + H int `json:"h"` + Ctaurl string `json:"ctaurl"` +} + +func extractAdmImage(adapterResponseAdm string) (string, error) { + var imgMarkup string + var err error + + var imageAd imageAd + err = json.Unmarshal([]byte(adapterResponseAdm), &imageAd) + var image = imageAd.Image + + if err == nil { + var clickEvent strings.Builder + var impressionTracker strings.Builder + + for _, clicktracker := range image.Clicktrackers { + clickEvent.WriteString("fetch(decodeURIComponent('" + url.QueryEscape(clicktracker) + "'.replace(/\\+/g, ' ')), " + + "{cache: 'no-cache'});") + } + + for _, impression := range image.Impressiontrackers { + + impressionTracker.WriteString(fmt.Sprintf(``, impression)) + } + + imgMarkup = fmt.Sprintf(`
%s
`, + &clickEvent, url.QueryEscape(image.Img.Ctaurl), image. + Img.URL, image.Img.W, image.Img. + H, &impressionTracker) + } + return imgMarkup, err +} diff --git a/adapters/smaato/image_test.go b/adapters/smaato/image_test.go new file mode 100644 index 00000000000..5f39c857201 --- /dev/null +++ b/adapters/smaato/image_test.go @@ -0,0 +1,44 @@ +package smaato + +import ( + "testing" +) + +func TestRenderAdMarkup(t *testing.T) { + type args struct { + adType adMarkupType + adapterResponseAdm string + } + expectedResult := `
` + + `` + + `` + + `
` + + tests := []struct { + testName string + args args + result string + }{ + {"imageTest", args{"Img", + "{\"image\":{\"img\":{\"url\":\"//prebid-test.smaatolabs.net/img/320x50.jpg\"," + + "\"w\":350,\"h\":50,\"ctaurl\":\"//prebid-test.smaatolabs.net/track/ctaurl/1\"}," + + "\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"]," + + "\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}"}, + expectedResult, + }, + } + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + got, err := renderAdMarkup(tt.args.adType, tt.args.adapterResponseAdm) + if err != nil { + t.Errorf("error rendering ad markup: %v", err) + } + if got != tt.result { + t.Errorf("renderAdMarkup() got = %v, result %v", got, tt.result) + } + }) + } +} diff --git a/adapters/smaato/params_test.go b/adapters/smaato/params_test.go new file mode 100644 index 00000000000..6c71cbe75c6 --- /dev/null +++ b/adapters/smaato/params_test.go @@ -0,0 +1,65 @@ +package smaato + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file intends to test static/bidder-params/smaato.json + +// These also validate the format of the external API: request.imp[i].bidRequestExt.smaato + +// TestValidParams makes sure that the Smaato schema accepts all imp.bidRequestExt fields which Smaato supports. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderSmaato, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected smaato params: %s \n Error: %s", validParam, err) + } + } +} + +// TestInvalidParams makes sure that the Smaato schema rejects all the imp.bidRequestExt fields which are not support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderSmaato, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"publisherId":"test-id-1234-smaato","adspaceId": "1123581321"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"publisherId":"test-id-1234-smaato"}`, + `{"adspaceId": "1123581321"}`, + `{"publisherId":false}`, + `{"adspaceId":false}`, + `{"publisherId":0,"adspaceId": 1123581321}`, + `{"publisherId":false,"adspaceId": true}`, + `{"instl": 0}`, + `{"secure": 0}`, + `{"adspaceId": "1123581321","instl": 0,"secure": 0}`, + `{"instl": 0,"secure": 0}`, + `{"publisherId":"test-id-1234-smaato","instl": 0,"secure": 0}`, +} diff --git a/adapters/smaato/richmedia.go b/adapters/smaato/richmedia.go new file mode 100644 index 00000000000..1c94a3555c1 --- /dev/null +++ b/adapters/smaato/richmedia.go @@ -0,0 +1,52 @@ +package smaato + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" +) + +type richMediaAd struct { + RichMedia richmedia `json:"richmedia"` +} +type mediadata struct { + Content string `json:"content"` + W int `json:"w"` + H int `json:"h"` +} + +type richmedia struct { + MediaData mediadata `json:"mediadata"` + Impressiontrackers []string `json:"impressiontrackers"` + Clicktrackers []string `json:"clicktrackers"` +} + +func extractAdmRichMedia(adapterResponseAdm string) (string, error) { + var richMediaMarkup string + var err error + + var richMediaAd richMediaAd + err = json.Unmarshal([]byte(adapterResponseAdm), &richMediaAd) + var richMedia = richMediaAd.RichMedia + + if err == nil { + var clickEvent strings.Builder + var impressionTracker strings.Builder + + for _, clicktracker := range richMedia.Clicktrackers { + clickEvent.WriteString("fetch(decodeURIComponent('" + url.QueryEscape(clicktracker) + "'), " + + "{cache: 'no-cache'});") + } + for _, impression := range richMedia.Impressiontrackers { + + impressionTracker.WriteString(fmt.Sprintf(``, impression)) + } + + richMediaMarkup = fmt.Sprintf(`
%s%s
`, + &clickEvent, + richMedia.MediaData.Content, + &impressionTracker) + } + return richMediaMarkup, err +} diff --git a/adapters/smaato/richmedia_test.go b/adapters/smaato/richmedia_test.go new file mode 100644 index 00000000000..20fa1ba353c --- /dev/null +++ b/adapters/smaato/richmedia_test.go @@ -0,0 +1,39 @@ +package smaato + +import ( + "testing" +) + +func TestExtractAdmRichMedia(t *testing.T) { + type args struct { + adType adMarkupType + adapterResponseAdm string + } + expectedResult := `
hello
` + + `
` + tests := []struct { + testName string + args args + result string + }{ + {"richmediaTest", args{"Richmedia", "{\"richmedia\":{\"mediadata\":{\"content\":\"
hello
\"," + + "" + "\"w\":350," + + "\"h\":50},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"]," + + "\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}"}, + expectedResult, + }, + } + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + got, err := renderAdMarkup(tt.args.adType, tt.args.adapterResponseAdm) + if err != nil { + t.Errorf("error rendering ad markup: %v", err) + } + if got != tt.result { + t.Errorf("renderAdMarkup() got = %v, result %v", got, tt.result) + } + }) + } +} diff --git a/adapters/smaato/smaato.go b/adapters/smaato/smaato.go new file mode 100644 index 00000000000..06678d77a61 --- /dev/null +++ b/adapters/smaato/smaato.go @@ -0,0 +1,276 @@ +package smaato + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/buger/jsonparser" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +const clientVersion = "prebid_server_0.1" + +type adMarkupType string + +const ( + smtAdTypeImg adMarkupType = "Img" + smtAdTypeRichmedia adMarkupType = "Richmedia" +) + +// SmaatoAdapter describes a Smaato prebid server adapter. +type SmaatoAdapter struct { + URI string +} + +//userExt defines User.Ext object for Smaato +type userExt struct { + Data userExtData `json:"data"` +} + +type userExtData struct { + Keywords string `json:"keywords"` + Gender string `json:"gender"` + Yob int64 `json:"yob"` +} + +//userExt defines Site.Ext object for Smaato +type siteExt struct { + Data siteExtData `json:"data"` +} + +type siteExtData struct { + Keywords string `json:"keywords"` +} + +// NewSmaatoBidder creates a Smaato bid adapter. +func NewSmaatoBidder(uri string) *SmaatoAdapter { + return &SmaatoAdapter{ + URI: uri, + } +} + +// MakeRequests makes the HTTP requests which should be made to fetch bids. +func (a *SmaatoAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + errs := make([]error, 0, len(request.Imp)) + if len(request.Imp) == 0 { + errs = append(errs, &errortypes.BadInput{Message: "no impressions in bid request"}) + return nil, errs + } + + // Use bidRequestExt of first imp to retrieve params which are valid for all imps, e.g. publisherId + publisherId, err := jsonparser.GetString(request.Imp[0].Ext, "bidder", "publisherId") + if err != nil { + errs = append(errs, err) + return nil, errs + } + + for i := 0; i < len(request.Imp); i++ { + err := parseImpressionObject(&request.Imp[i]) + // If the parsing is failed, remove imp and add the error. + if err != nil { + errs = append(errs, err) + request.Imp = append(request.Imp[:i], request.Imp[i+1:]...) + i-- + } + } + if request.Site != nil { + siteCopy := *request.Site + siteCopy.Publisher = &openrtb.Publisher{ID: publisherId} + + if request.Site.Ext != nil { + var siteExt siteExt + err := json.Unmarshal([]byte(request.Site.Ext), &siteExt) + if err != nil { + errs = append(errs, err) + return nil, errs + } + siteCopy.Keywords = siteExt.Data.Keywords + siteCopy.Ext = nil + } + request.Site = &siteCopy + } + + if request.User != nil && request.User.Ext != nil { + var userExt userExt + var userExtRaw map[string]json.RawMessage + + rawExtErr := json.Unmarshal(request.User.Ext, &userExtRaw) + if rawExtErr != nil { + errs = append(errs, rawExtErr) + return nil, errs + } + + userExtErr := json.Unmarshal([]byte(request.User.Ext), &userExt) + if userExtErr != nil { + errs = append(errs, userExtErr) + return nil, errs + } + + userCopy := *request.User + extractUserExtAttributes(userExt, &userCopy) + delete(userExtRaw, "data") + userCopy.Ext, err = json.Marshal(userExtRaw) + if err != nil { + errs = append(errs, err) + return nil, errs + } + request.User = &userCopy + } + + // Setting ext client info + type bidRequestExt struct { + Client string `json:"client"` + } + request.Ext, err = json.Marshal(bidRequestExt{Client: clientVersion}) + if err != nil { + errs = append(errs, err) + return nil, errs + } + reqJSON, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + uri := a.URI + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return []*adapters.RequestData{{ + Method: "POST", + Uri: uri, + Body: reqJSON, + Headers: headers, + }}, errs +} + +// MakeBids unpacks the server's response into Bids. +func (a *SmaatoAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{fmt.Errorf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + + var markupError error + bid.AdM, markupError = renderAdMarkup(getAdMarkupType(response, bid.AdM), bid.AdM) + if markupError != nil { + fmt.Println(markupError) + continue // no bid when broken ad markup + } + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: openrtb_ext.BidTypeBanner, + }) + } + } + return bidResponse, nil +} + +func renderAdMarkup(adMarkupType adMarkupType, adMarkup string) (string, error) { + var markupError error + var adm string + switch adMarkupType { + case smtAdTypeImg: + adm, markupError = extractAdmImage(adMarkup) + case smtAdTypeRichmedia: + adm, markupError = extractAdmRichMedia(adMarkup) + default: + return "", fmt.Errorf("Unknown markup type %s", adMarkupType) + } + return adm, markupError +} + +func getAdMarkupType(response *adapters.ResponseData, adMarkup string) adMarkupType { + if admType := adMarkupType(response.Headers.Get("X-SMT-ADTYPE")); admType != "" { + return admType + } + if strings.HasPrefix(adMarkup, `{"image":`) { + return smtAdTypeImg + } + if strings.HasPrefix(adMarkup, `{"richmedia":`) { + return smtAdTypeRichmedia + } + return "" +} + +func assignBannerSize(banner *openrtb.Banner) (*openrtb.Banner, error) { + if banner.W != nil && banner.H != nil { + return banner, nil + } + if len(banner.Format) == 0 { + return banner, fmt.Errorf("No sizes provided for Banner %v", banner.Format) + } + bannerCopy := *banner + bannerCopy.W = new(uint64) + *bannerCopy.W = banner.Format[0].W + bannerCopy.H = new(uint64) + *bannerCopy.H = banner.Format[0].H + + return &bannerCopy, nil +} + +// parseImpressionObject parse the imp to get it ready to send to smaato +func parseImpressionObject(imp *openrtb.Imp) error { + adSpaceID, err := jsonparser.GetString(imp.Ext, "bidder", "adspaceId") + if err != nil { + return err + } + + // SMAATO supports banner impressions. + if imp.Banner != nil { + bannerCopy, err := assignBannerSize(imp.Banner) + if err != nil { + return err + } + imp.Banner = bannerCopy + imp.TagID = adSpaceID + imp.Ext = nil + return nil + } + return fmt.Errorf("invalid MediaType. SMAATO only supports Banner. Ignoring ImpID=%s", imp.ID) +} + +func extractUserExtAttributes(userExt userExt, userCopy *openrtb.User) { + gender := userExt.Data.Gender + if gender != "" { + userCopy.Gender = gender + } + + yob := userExt.Data.Yob + if yob != 0 { + userCopy.Yob = yob + } + + keywords := userExt.Data.Keywords + if keywords != "" { + userCopy.Keywords = keywords + } +} diff --git a/adapters/smaato/smaato_test.go b/adapters/smaato/smaato_test.go new file mode 100644 index 00000000000..cf76d58de2c --- /dev/null +++ b/adapters/smaato/smaato_test.go @@ -0,0 +1,11 @@ +package smaato + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "smaatotest", NewSmaatoBidder("https://prebid/bidder")) +} diff --git a/adapters/smaato/smaatotest/exemplary/simple-banner-richMedia.json b/adapters/smaato/smaatotest/exemplary/simple-banner-richMedia.json new file mode 100644 index 00000000000..7b662e8813a --- /dev/null +++ b/adapters/smaato/smaatotest/exemplary/simple-banner-richMedia.json @@ -0,0 +1,194 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "ext": { + "data": { + "keywords": "power tools", + "search": "drill", + "content": { + "userrating": 4 + } + } + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "ext": { + "consent": "gdprConsentString", + "data": { + "keywords": "a,b", + "gender": "M", + "yob": 1984, + "geo": { + "country": "ca" + } + } + } + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "https://prebid/bidder", + "body": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "tagid": "130563103", + "banner": { + "h": 50, + "w": 320, + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + } + } + ], + "user": { + "gender": "M", + "keywords": "a,b", + "yob": 1984, + "ext": { + "consent": "gdprConsentString" + } + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + }, + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "keywords": "power tools" + }, + "ext": { + "client": "prebid_server_0.1" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "5ebea288-f13a-4754-be6d-4ade66c68877", + "seatbid": [ + { + "seat": "CM6523", + "bid": [ + { + "adm": "{\"richmedia\":{\"mediadata\":{\"content\":\"
hello
\", \"w\":350,\"h\":50},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}", + "adomain": [ + "smaato.com" + ], + "bidderName": "smaato", + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + } + ] + } + ], + "bidid": "04db8629-179d-4bcd-acce-e54722969006", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "
hello
\"\"\"\"
", + "adomain": [ + "smaato.com" + ], + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/exemplary/simple-banner.json b/adapters/smaato/smaatotest/exemplary/simple-banner.json new file mode 100644 index 00000000000..a50fd9289e3 --- /dev/null +++ b/adapters/smaato/smaatotest/exemplary/simple-banner.json @@ -0,0 +1,190 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "ext": { + "data": { + "keywords": "power tools", + "search": "drill", + "content": { + "userrating": 4 + } + } + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "ext": { + "consent": "gdprConsentString", + "data": { + "keywords": "a,b", + "gender": "M", + "yob": 1984, + "geo": { + "country": "ca" + } + } + } + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"] + }, + "uri": "https://prebid/bidder", + "body": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "tagid": "130563103", + "banner": { + "h": 50, + "w": 320, + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + } + } + ], + "user": { + "ext": { + "consent": "gdprConsentString" + }, + "gender": "M", + "keywords": "a,b", + "yob": 1984 + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + }, + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "keywords": "power tools" + }, + "ext": { + "client": "prebid_server_0.1" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "5ebea288-f13a-4754-be6d-4ade66c68877", + "seatbid": [ + { + "seat": "CM6523", + "bid": [ + { + "adm": "{\"image\":{\"img\":{\"url\":\"//prebid-test.smaatolabs.net/img/320x50.jpg\",\"w\":350,\"h\":50,\"ctaurl\":\"//prebid-test.smaatolabs.net/track/ctaurl/1\"},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}", + "adomain": [ + "smaato.com" + ], + "bidderName": "smaato", + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + } + ] + } + ], + "bidid": "04db8629-179d-4bcd-acce-e54722969006", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "
\"\"\"\"
", + "adomain": [ + "smaato.com" + ], + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/params/banner.json b/adapters/smaato/smaatotest/params/banner.json new file mode 100644 index 00000000000..a84c44d4d8e --- /dev/null +++ b/adapters/smaato/smaatotest/params/banner.json @@ -0,0 +1,4 @@ +{ + "publisherId": "1100042525", + "adspaceId": "130563103" +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-adm-response.json b/adapters/smaato/smaatotest/supplemental/bad-adm-response.json new file mode 100644 index 00000000000..6d4990e9ea4 --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/bad-adm-response.json @@ -0,0 +1,166 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "ext": { + "data": { + "keywords": "power tools", + "search": "drill", + "content": { + "userrating": 4 + } + } + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "ext": { + "consent": "gdprConsentString", + "data": { + "keywords": "a,b", + "gender": "M", + "yob": 1984, + "geo": { + "country": "ca" + } + } + } + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"] + }, + "uri": "https://prebid/bidder", + "body": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "tagid": "130563103", + "banner": { + "h": 50, + "w": 320, + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + } + } + ], + "user": { + "ext": { + "consent": "gdprConsentString" + }, + "gender": "M", + "keywords": "a,b", + "yob": 1984 + }, + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + }, + "site": { + "publisher": { + "id": "1100042525" + }, + "page": "http://localhost:3000/server.html?pbjs_debug=true&endpoint=http://localhost:3000/bidder", + "keywords": "power tools" + }, + "ext": { + "client": "prebid_server_0.1" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "5ebea288-f13a-4754-be6d-4ade66c68877", + "seatbid": [ + { + "seat": "CM6523", + "bid": [ + { + "adm": "{\"badmedia\":{\"mediadata\":{\"content\":\"
hello
\", \"w\":350,\"h\":50},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}", + "adomain": [ + "smaato.com" + ], + "bidderName": "smaato", + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + } + ] + } + ], + "bidid": "04db8629-179d-4bcd-acce-e54722969006", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-ext-req.json b/adapters/smaato/smaatotest/supplemental/bad-ext-req.json new file mode 100644 index 00000000000..0c970fc5bad --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/bad-ext-req.json @@ -0,0 +1,54 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "ext": { + "consent": "gdprConsentString" + } + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-imp-banner-format-req.json b/adapters/smaato/smaatotest/supplemental/bad-imp-banner-format-req.json new file mode 100644 index 00000000000..b9560f0f9ca --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/bad-imp-banner-format-req.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": ["application/json;charset=utf-8"], + "Accept": ["application/json"] + }, + "uri": "https://prebid/bidder", + "body": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [], + "site": { + "page": "prebid.org", + "publisher": { + "id": "1100042525" + } + }, + "ext": { + "client": "prebid_server_0.1" + } + } + } + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "No sizes provided for Banner []", + "comparison": "literal" + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 0. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-user-ext-data-req.json b/adapters/smaato/smaatotest/supplemental/bad-user-ext-data-req.json new file mode 100644 index 00000000000..9e65fce1c3e --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/bad-user-ext-data-req.json @@ -0,0 +1,67 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "gender": "M", + "ext": { + "data": { + "keywords":"a,b", + "gender": "M", + "yob": "", + "geo": { + "country": "ca" + } + }, + "consent":"yes" + } + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go struct field userExtData.data.yob of type int64", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-user-ext-req.json b/adapters/smaato/smaatotest/supplemental/bad-user-ext-req.json new file mode 100644 index 00000000000..7f05b2dff14 --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/bad-user-ext-req.json @@ -0,0 +1,57 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "user": { + "gender": "M", + "ext": 99 + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString" + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal number into Go value of type map[string]json.RawMessage", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/no-consent-info.json b/adapters/smaato/smaatotest/supplemental/no-consent-info.json new file mode 100644 index 00000000000..9e0ccfdcdde --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/no-consent-info.json @@ -0,0 +1,137 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + }, + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "https://prebid/bidder", + "body": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "imp": [ + { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "tagid": "130563103", + "banner": { + "h": 50, + "w": 320, + "format": [ + { + "w": 320, + "h": 50 + }, + { + "w": 320, + "h": 250 + } + ] + } + } + ], + "site": { + "page": "prebid.org", + "publisher": { + "id": "1100042525" + } + }, + "ext": { + "client": "prebid_server_0.1" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "5ebea288-f13a-4754-be6d-4ade66c68877", + "seatbid": [ + { + "seat": "CM6523", + "bid": [ + { + "adm": "{\"image\":{\"img\":{\"url\":\"//prebid-test.smaatolabs.net/img/320x50.jpg\",\"w\":350,\"h\":50,\"ctaurl\":\"//prebid-test.smaatolabs.net/track/ctaurl/1\"},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}", + "adomain": [ + "smaato.com" + ], + "bidderName": "smaato", + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + } + ] + } + ], + "bidid": "04db8629-179d-4bcd-acce-e54722969006", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "
\"\"\"\"
", + "adomain": [ + "smaato.com" + ], + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://bidstalkcreatives.s3.amazonaws.com/1x1.png", + "nurl": "https://ets-eu-west-1.track.smaato.net/v1/view?sessionId=e4e17adb-9599-42b1-bb5f-a1f1b3bee572&adSourceId=6906aae8-7f74-4edd-9a4f-f49379a3cadd&originalRequestTime=1552310449698&expires=1552311350698&winurl=ama8JbpJVpFWxvEja5viE3cLXFu58qRI8dGUh23xtsOn3N2-5UU0IwkgNEmR82pI37fcMXejL5IWTNAoW6Cnsjf-Dxl_vx2dUqMrVEevX-Vdx2VVnf-D5f73gZhvi4t36iPL8Dsw4aACekoLvVOV7-eXDjz7GHy60QFqcwKf5g2AlKPOInyZ6vJg_fn4qA9argvCRgwVybXE9Ndm2W0v8La4uFYWpJBOUveDDUrSQfzal7RsYvLb_OyaMlPHdrd_bwA9qqZWuyJXd-L9lxr7RQ%3D%3D%7CMw3kt91KJR0Uy5L-oNztAg%3D%3D&dpid=4XVofb_lH-__hr2JNGhKfg%3D%3D%7Cr9ciCU1cx3zmHXihItKO0g%3D%3D", + "price": 0.01, + "w": 350, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/no-imp-req.json b/adapters/smaato/smaatotest/supplemental/no-imp-req.json new file mode 100644 index 00000000000..bfaf51e6ea8 --- /dev/null +++ b/adapters/smaato/smaatotest/supplemental/no-imp-req.json @@ -0,0 +1,17 @@ +{ + "mockBidRequest": { + "id": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "site": { + "page": "prebid.org", + "publisher": { + "id": "1" + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "no impressions in bid request", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index 67689d1ab1a..fb0607646ee 100755 --- a/config/config.go +++ b/config/config.go @@ -845,6 +845,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rubicon.disabled", true) v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json") v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") + v.SetDefault("adapters.smaato.endpoint", "https://prebid.ad.smaato.net/oapi/prebid") v.SetDefault("adapters.smartadserver.endpoint", "https://ssb.smartadserver.com") v.SetDefault("adapters.smartrtb.endpoint", "http://market-east.smrtb.com/json/publisher/rtb?pubid={{.PublisherID}}") v.SetDefault("adapters.somoaudience.endpoint", "http://publisher-east.mobileadtrading.com/rtb/bid") diff --git a/docs/bidders/smaato.md b/docs/bidders/smaato.md new file mode 100644 index 00000000000..881f8f2ab54 --- /dev/null +++ b/docs/bidders/smaato.md @@ -0,0 +1,42 @@ + +# Smaato Bidder + +``` +Module Name: Smaato Bidder Adapter +Module Type: Bidder Adapter +Maintainer: prebid@smaato.com +``` + +### Description + +Please contact Smaato Support or prebid@smaato.com to get set up with a publisherId and adspaceId. + +### Test Parameters: + +Following example includes sample `imp` object with publisherId and adSlot which can be used to test Smaato Adapter + +``` +"imp":[ + { + "id":“1C86242D-9535-47D6-9576-7B1FE87F282C, + "banner":{ + "format":[ + { + "w":300, + "h":50 + }, + { + "w":300, + "h":250 + } + ] + }, + "ext":{ + "smaato":{ + "publisherId":"100042525", + "adspaceId":"130563103" + } + } + } + ] +``` diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 2ecddb83cfc..207a7a9b9e9 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -64,6 +64,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/smaato" "github.com/prebid/prebid-server/adapters/smartadserver" "github.com/prebid/prebid-server/adapters/smartrtb" "github.com/prebid/prebid-server/adapters/somoaudience" @@ -154,6 +155,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker), openrtb_ext.BidderSharethrough: sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint), + openrtb_ext.BidderSmaato: smaato.NewSmaatoBidder(cfg.Adapters[string(openrtb_ext.BidderSmaato)].Endpoint), openrtb_ext.BidderSmartadserver: smartadserver.NewSmartadserverBidder(cfg.Adapters[string(openrtb_ext.BidderSmartadserver)].Endpoint), openrtb_ext.BidderSmartRTB: smartrtb.NewSmartRTBBidder(cfg.Adapters[string(openrtb_ext.BidderSmartRTB)].Endpoint), openrtb_ext.BidderSomoaudience: somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 62fb9750616..ee0f40903e0 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -80,6 +80,7 @@ const ( BidderRTBHouse BidderName = "rtbhouse" BidderRubicon BidderName = "rubicon" BidderSharethrough BidderName = "sharethrough" + BidderSmaato BidderName = "smaato" BidderSmartadserver BidderName = "smartadserver" BidderSmartRTB BidderName = "smartrtb" BidderSomoaudience BidderName = "somoaudience" @@ -162,6 +163,7 @@ var BidderMap = map[string]BidderName{ "rtbhouse": BidderRTBHouse, "rubicon": BidderRubicon, "sharethrough": BidderSharethrough, + "smaato": BidderSmaato, "smartadserver": BidderSmartadserver, "smartrtb": BidderSmartRTB, "somoaudience": BidderSomoaudience, diff --git a/openrtb_ext/imp_smaato.go b/openrtb_ext/imp_smaato.go new file mode 100644 index 00000000000..10de97fb017 --- /dev/null +++ b/openrtb_ext/imp_smaato.go @@ -0,0 +1,9 @@ +package openrtb_ext + +// ExtImpSmaato defines the contract for bidrequest.imp[i].ext.smaato +// PublisherId and AdSpaceId are mandatory parameters, others are optional parameters +// AdSpaceId is identifier for specific ad placement or ad tag +type ExtImpSmaato struct { + PublisherID string `json:"publisherId"` + AdSpaceID string `json:"adspaceId"` +} diff --git a/static/bidder-info/smaato.yaml b/static/bidder-info/smaato.yaml new file mode 100644 index 00000000000..662603febdb --- /dev/null +++ b/static/bidder-info/smaato.yaml @@ -0,0 +1,9 @@ +maintainer: + email: "prebid@smaato.com" +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner \ No newline at end of file diff --git a/static/bidder-params/smaato.json b/static/bidder-params/smaato.json new file mode 100644 index 00000000000..aa91c4bacc5 --- /dev/null +++ b/static/bidder-params/smaato.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Smaato Adapter Params", + "description": "A schema which validates params accepted by the Smaato adapter", + "type": "object", + "properties": { + "publisherId": { + "type": "string", + "description": "A unique identifier for this impression within the context of the bid request" + }, + "adspaceId": { + "type": "string", + "description": "Identifier for specific ad placement is SOMA `adspaceId`" + } + }, + "required": ["publisherId","adspaceId"] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 32ab2e730eb..22b215c3132 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -93,6 +93,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderMobileFuse: true, openrtb_ext.BidderOrbidder: true, openrtb_ext.BidderPubnative: true, + openrtb_ext.BidderSmaato: true, openrtb_ext.BidderTappx: true, openrtb_ext.BidderYeahmobi: true, } From 7615d472149c9dd7ffd06360e54792cd54a0d51f Mon Sep 17 00:00:00 2001 From: Adprime <64427228+Adprime@users.noreply.github.com> Date: Thu, 6 Aug 2020 19:43:30 +0300 Subject: [PATCH 157/318] New Adprime adapter (#1418) Co-authored-by: Aiholkin --- adapters/adprime/adprime.go | 142 ++++++++++++++++++ adapters/adprime/adprime_test.go | 12 ++ .../adprimetest/exemplary/simple-banner.json | 134 +++++++++++++++++ .../adprimetest/exemplary/simple-video.json | 119 +++++++++++++++ .../exemplary/simple-web-banner.json | 133 ++++++++++++++++ .../adprime/adprimetest/params/banner.json | 3 + .../adprimetest/params/race/banner.json | 3 + .../adprimetest/params/race/video.json | 3 + .../adprime/adprimetest/params/video.json | 3 + .../adprimetest/supplemental/bad-imp-ext.json | 42 ++++++ .../supplemental/bad_response.json | 85 +++++++++++ .../supplemental/no-imp-ext-1.json | 39 +++++ .../supplemental/no-imp-ext-2.json | 39 +++++ .../adprimetest/supplemental/status-204.json | 79 ++++++++++ .../adprimetest/supplemental/status-404.json | 85 +++++++++++ adapters/adprime/params_test.go | 46 ++++++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adprime.go | 6 + static/bidder-info/adprime.yaml | 11 ++ static/bidder-params/adprime.json | 14 ++ usersync/usersyncers/syncer_test.go | 1 + 23 files changed, 1004 insertions(+) create mode 100644 adapters/adprime/adprime.go create mode 100644 adapters/adprime/adprime_test.go create mode 100644 adapters/adprime/adprimetest/exemplary/simple-banner.json create mode 100644 adapters/adprime/adprimetest/exemplary/simple-video.json create mode 100644 adapters/adprime/adprimetest/exemplary/simple-web-banner.json create mode 100644 adapters/adprime/adprimetest/params/banner.json create mode 100644 adapters/adprime/adprimetest/params/race/banner.json create mode 100644 adapters/adprime/adprimetest/params/race/video.json create mode 100644 adapters/adprime/adprimetest/params/video.json create mode 100644 adapters/adprime/adprimetest/supplemental/bad-imp-ext.json create mode 100644 adapters/adprime/adprimetest/supplemental/bad_response.json create mode 100644 adapters/adprime/adprimetest/supplemental/no-imp-ext-1.json create mode 100644 adapters/adprime/adprimetest/supplemental/no-imp-ext-2.json create mode 100644 adapters/adprime/adprimetest/supplemental/status-204.json create mode 100644 adapters/adprime/adprimetest/supplemental/status-404.json create mode 100644 adapters/adprime/params_test.go create mode 100644 openrtb_ext/imp_adprime.go create mode 100644 static/bidder-info/adprime.yaml create mode 100644 static/bidder-params/adprime.json diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go new file mode 100644 index 00000000000..007d3c86570 --- /dev/null +++ b/adapters/adprime/adprime.go @@ -0,0 +1,142 @@ +package adprime + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/buger/jsonparser" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +// AdprimeAdapter struct +type AdprimeAdapter struct { + URI string +} + +// NewAdprimeBidder Initializes the Bidder +func NewAdprimeBidder(endpoint string) *AdprimeAdapter { + return &AdprimeAdapter{ + URI: endpoint, + } +} + +type adprimeParams struct { + TagID string `json:"TagID"` +} + +// MakeRequests create bid request for adprime demand +func (a *AdprimeAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var err error + var tagID string + + var adapterRequests []*adapters.RequestData + + reqCopy := *request + for _, imp := range request.Imp { + reqCopy.Imp = []openrtb.Imp{imp} + + tagID, err = jsonparser.GetString(reqCopy.Imp[0].Ext, "bidder", "TagID") + if err != nil { + errs = append(errs, err) + continue + } + + reqCopy.Imp[0].TagID = tagID + + adapterReq, errors := a.makeRequest(&reqCopy) + if adapterReq != nil { + adapterRequests = append(adapterRequests, adapterReq) + } + errs = append(errs, errors...) + } + return adapterRequests, errs +} + +func (a *AdprimeAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) { + + var errs []error + + reqJSON, err := json.Marshal(request) + + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + return &adapters.RequestData{ + Method: "POST", + Uri: a.URI, + Body: reqJSON, + Headers: headers, + }, errs +} + +// MakeBids makes the bids +func (a *AdprimeAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusNotFound { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Page not found: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner == nil && imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } + return mediaType, nil + } + } + + // This shouldnt happen. Lets handle it just incase by returning an error. + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID), + } +} diff --git a/adapters/adprime/adprime_test.go b/adapters/adprime/adprime_test.go new file mode 100644 index 00000000000..2d3ee9b2b3f --- /dev/null +++ b/adapters/adprime/adprime_test.go @@ -0,0 +1,12 @@ +package adprime + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adprimeAdapter := NewAdprimeBidder("http://delta.adprime.com/?c=o&m=ortb") + adapterstest.RunJSONBidderTest(t, "adprimetest", adprimeAdapter) +} diff --git a/adapters/adprime/adprimetest/exemplary/simple-banner.json b/adapters/adprime/adprimetest/exemplary/simple-banner.json new file mode 100644 index 00000000000..076175c6274 --- /dev/null +++ b/adapters/adprime/adprimetest/exemplary/simple-banner.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "adprime" + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adprime/adprimetest/exemplary/simple-video.json b/adapters/adprime/adprimetest/exemplary/simple-video.json new file mode 100644 index 00000000000..3e61c4dddd1 --- /dev/null +++ b/adapters/adprime/adprimetest/exemplary/simple-video.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "TagID": "288" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "tagid": "288", + "ext": { + "bidder": { + "TagID": "288" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "adprime" + } + ], + "cur": "USD" + } + } + } + ], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/adprime/adprimetest/exemplary/simple-web-banner.json b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json new file mode 100644 index 00000000000..a955854fb31 --- /dev/null +++ b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json @@ -0,0 +1,133 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "adprime" + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/adprime/adprimetest/params/banner.json b/adapters/adprime/adprimetest/params/banner.json new file mode 100644 index 00000000000..e3f4cb7605a --- /dev/null +++ b/adapters/adprime/adprimetest/params/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "1" +} \ No newline at end of file diff --git a/adapters/adprime/adprimetest/params/race/banner.json b/adapters/adprime/adprimetest/params/race/banner.json new file mode 100644 index 00000000000..e3f4cb7605a --- /dev/null +++ b/adapters/adprime/adprimetest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "1" +} \ No newline at end of file diff --git a/adapters/adprime/adprimetest/params/race/video.json b/adapters/adprime/adprimetest/params/race/video.json new file mode 100644 index 00000000000..c8d14757903 --- /dev/null +++ b/adapters/adprime/adprimetest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "288" +} \ No newline at end of file diff --git a/adapters/adprime/adprimetest/params/video.json b/adapters/adprime/adprimetest/params/video.json new file mode 100644 index 00000000000..c8d14757903 --- /dev/null +++ b/adapters/adprime/adprimetest/params/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "288" +} \ No newline at end of file diff --git a/adapters/adprime/adprimetest/supplemental/bad-imp-ext.json b/adapters/adprime/adprimetest/supplemental/bad-imp-ext.json new file mode 100644 index 00000000000..a95c56e8426 --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/bad-imp-ext.json @@ -0,0 +1,42 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "adprime": { + "TagID": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, +"expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } +] +} diff --git a/adapters/adprime/adprimetest/supplemental/bad_response.json b/adapters/adprime/adprimetest/supplemental/bad_response.json new file mode 100644 index 00000000000..329e9c7269f --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/bad_response.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 200, + "body": "" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/adprime/adprimetest/supplemental/no-imp-ext-1.json b/adapters/adprime/adprimetest/supplemental/no-imp-ext-1.json new file mode 100644 index 00000000000..1e38dbe4541 --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/no-imp-ext-1.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": "" + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/adprime/adprimetest/supplemental/no-imp-ext-2.json b/adapters/adprime/adprimetest/supplemental/no-imp-ext-2.json new file mode 100644 index 00000000000..f9759fae8ff --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/no-imp-ext-2.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": {} + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/adprime/adprimetest/supplemental/status-204.json b/adapters/adprime/adprimetest/supplemental/status-204.json new file mode 100644 index 00000000000..44ee59d4d28 --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/status-204.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }] +} diff --git a/adapters/adprime/adprimetest/supplemental/status-404.json b/adapters/adprime/adprimetest/supplemental/status-404.json new file mode 100644 index 00000000000..c2b303f0cb4 --- /dev/null +++ b/adapters/adprime/adprimetest/supplemental/status-404.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://delta.adprime.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 404, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Page not found: 404. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adprime/params_test.go b/adapters/adprime/params_test.go new file mode 100644 index 00000000000..05adad5c4ff --- /dev/null +++ b/adapters/adprime/params_test.go @@ -0,0 +1,46 @@ +package adprime + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// TestValidParams makes sure that the adprime schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdprime, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adprime params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the adprime schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdprime, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"TagID": "1"}`, +} + +var invalidParams = []string{ + `{"id": "123"}`, + `{"tagid": "123"}`, + `{"TagID": 16}`, +} diff --git a/config/config.go b/config/config.go index fb0607646ee..9663b021b5b 100755 --- a/config/config.go +++ b/config/config.go @@ -797,6 +797,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adocean.endpoint", "https://{{.Host}}") v.SetDefault("adapters.adoppler.endpoint", "http://app.trustedmarketplace.io/ads") v.SetDefault("adapters.adpone.endpoint", "http://rtb.adpone.com/bid-request?src=prebid_server") + v.SetDefault("adapters.adprime.endpoint", "http://delta.adprime.com/?c=o&m=ortb") v.SetDefault("adapters.adtarget.endpoint", "http://ghb.console.adtarget.com.tr/pbs/ortb") v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb") v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 207a7a9b9e9..d056de664b7 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -19,6 +19,7 @@ import ( "github.com/prebid/prebid-server/adapters/adocean" "github.com/prebid/prebid-server/adapters/adoppler" "github.com/prebid/prebid-server/adapters/adpone" + "github.com/prebid/prebid-server/adapters/adprime" "github.com/prebid/prebid-server/adapters/adtarget" "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" @@ -106,6 +107,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdOcean: adocean.NewAdOceanBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderAdOcean))].Endpoint), openrtb_ext.BidderAdoppler: adoppler.NewAdopplerBidder(cfg.Adapters[string(openrtb_ext.BidderAdoppler)].Endpoint), openrtb_ext.BidderAdpone: adpone.NewAdponeBidder(cfg.Adapters[string(openrtb_ext.BidderAdpone)].Endpoint), + openrtb_ext.BidderAdprime: adprime.NewAdprimeBidder(cfg.Adapters[string(openrtb_ext.BidderAdprime)].Endpoint), openrtb_ext.BidderAdtarget: adtarget.NewAdtargetBidder(cfg.Adapters[string(openrtb_ext.BidderAdtarget)].Endpoint), openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), openrtb_ext.BidderAdvangelists: advangelists.NewAdvangelistsBidder(cfg.Adapters[string(openrtb_ext.BidderAdvangelists)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index ee0f40903e0..761f53d441e 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -34,6 +34,7 @@ const ( BidderAdman BidderName = "adman" BidderAdmixer BidderName = "admixer" BidderAdOcean BidderName = "adocean" + BidderAdprime BidderName = "adprime" BidderAdtarget BidderName = "adtarget" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" @@ -116,6 +117,7 @@ var BidderMap = map[string]BidderName{ "adman": BidderAdman, "admixer": BidderAdmixer, "adocean": BidderAdOcean, + "adprime": BidderAdprime, "adpone": BidderAdpone, "adtarget": BidderAdtarget, "adtelligent": BidderAdtelligent, diff --git a/openrtb_ext/imp_adprime.go b/openrtb_ext/imp_adprime.go new file mode 100644 index 00000000000..a089b818b56 --- /dev/null +++ b/openrtb_ext/imp_adprime.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpAdprime defines adprime specifiec param +type ExtImpAdprime struct { + TagID string `json:"TagID"` +} diff --git a/static/bidder-info/adprime.yaml b/static/bidder-info/adprime.yaml new file mode 100644 index 00000000000..9759ed63be7 --- /dev/null +++ b/static/bidder-info/adprime.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "rafal@adprime.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-params/adprime.json b/static/bidder-params/adprime.json new file mode 100644 index 00000000000..d527056597d --- /dev/null +++ b/static/bidder-params/adprime.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adprime Adapter Params", + "description": "A schema which validates params accepted by the Adprime adapter", + + "type": "object", + "properties": { + "TagID": { + "type": "string", + "description": "An ID which identifies the adprime ad tag" + } + }, + "required" : [ "TagID" ] + } \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 22b215c3132..9197ed9507d 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -96,6 +96,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderSmaato: true, openrtb_ext.BidderTappx: true, openrtb_ext.BidderYeahmobi: true, + openrtb_ext.BidderAdprime: true, } for bidder, config := range cfg.Adapters { From a7aaa97af15618f1b4cb7de3cb38866213c41028 Mon Sep 17 00:00:00 2001 From: guscarreon Date: Thu, 6 Aug 2020 14:21:01 -0400 Subject: [PATCH 158/318] Separate "debug" behavior from "billable" behavior (#1387) --- exchange/bidder.go | 2 +- exchange/bidder_test.go | 48 ----- exchange/exchange.go | 99 ++++----- exchange/exchange_test.go | 190 ++++++++++++++++-- exchange/utils.go | 98 ++++++--- exchange/utils_test.go | 412 ++++++++++++++++++++++++++++++++------ openrtb_ext/request.go | 1 + 7 files changed, 646 insertions(+), 204 deletions(-) diff --git a/exchange/bidder.go b/exchange/bidder.go index 7c39b72b348..decad8ccf2f 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -150,7 +150,7 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi for i := 0; i < len(reqData); i++ { httpInfo := <-responseChannel // If this is a test bid, capture debugging info from the requests. - if request.Test == 1 { + if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) { seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo)) } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 1a27b72aa12..7ae96c09b93 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -938,54 +938,6 @@ func TestSuccessfulResponseLogging(t *testing.T) { } } -// TestServerCallDebugging makes sure that we log the server calls made by the Bidder on test bids. -func TestServerCallDebugging(t *testing.T) { - respBody := "{\"bid\":false}" - respStatus := 200 - server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) - defer server.Close() - - reqBody := "{\"key\":\"val\"}" - reqUrl := server.URL - bidderImpl := &goodSingleBidder{ - httpRequest: &adapters.RequestData{ - Method: "POST", - Uri: reqUrl, - Body: []byte(reqBody), - Headers: http.Header{}, - }, - } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() - - bids, _ := bidder.requestBid( - context.Background(), - &openrtb.BidRequest{ - Test: 1, - }, - "test", - 1.0, - currencyConverter.Rates(), - &adapters.ExtraRequestInfo{}, - ) - - if len(bids.httpCalls) != 1 { - t.Errorf("We should log the server call if this is a test bid. Got %d", len(bids.httpCalls)) - } - if bids.httpCalls[0].Uri != reqUrl { - t.Errorf("Wrong httpcalls URI. Expected %s, got %s", reqUrl, bids.httpCalls[0].Uri) - } - if bids.httpCalls[0].RequestBody != reqBody { - t.Errorf("Wrong httpcalls RequestBody. Expected %s, got %s", reqBody, bids.httpCalls[0].RequestBody) - } - if bids.httpCalls[0].ResponseBody != respBody { - t.Errorf("Wrong httpcalls ResponseBody. Expected %s, got %s", respBody, bids.httpCalls[0].ResponseBody) - } - if bids.httpCalls[0].Status != respStatus { - t.Errorf("Wrong httpcalls Status. Expected %d, got %d", respStatus, bids.httpCalls[0].Status) - } -} - func TestMobileNativeTypes(t *testing.T) { respBody := "{\"bid\":false}" respStatus := 200 diff --git a/exchange/exchange.go b/exchange/exchange.go index 5001e495440..ad591f57794 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -27,6 +27,10 @@ import ( "github.com/prebid/prebid-server/prebid_cache_client" ) +type ContextKey string + +const DebugContextKey = ContextKey("debugInfo") + // Exchange runs Auctions. Implementations must be threadsafe, and will be shared across many goroutines. type Exchange interface { // HoldAuction executes an OpenRTB v2.5 Auction. @@ -86,12 +90,25 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con } func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) { - // Snapshot of resolved bid request for debug if test request - resolvedRequest, err := buildResolvedRequest(bidRequest) + + requestExt, err := extractBidRequestExt(bidRequest) if err != nil { - glog.Errorf("Error marshalling bid request for debug: %v", err) + return nil, err + } + + shouldCacheBids, shouldCacheVAST := getExtCacheInfo(requestExt) + targData := getExtTargetData(requestExt, shouldCacheBids, shouldCacheVAST) + if targData != nil { + targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() + } + + debugInfo := getDebugInfo(bidRequest, requestExt) + if debugInfo { + ctx = e.makeDebugContext(ctx, debugInfo) } + bidAdjustmentFactors := getExtBidAdjustmentFactors(requestExt) + for _, impInRequest := range bidRequest.Imp { var impLabels pbsmetrics.ImpLabels = pbsmetrics.ImpLabels{ BannerImps: impInRequest.Banner != nil, @@ -104,46 +121,16 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) e.me.RecordRequestPrivacy(privacyLabels) // List of bidders we have requests for. liveAdapters := listBiddersWithRequests(cleanRequests) - // Process the request to check for targeting parameters. - var targData *targetData - shouldCacheBids := false - shouldCacheVAST := false - var bidAdjustmentFactors map[string]float64 - var requestExt openrtb_ext.ExtRequest - if len(bidRequest.Ext) > 0 { - err := json.Unmarshal(bidRequest.Ext, &requestExt) - if err != nil { - return nil, fmt.Errorf("Error decoding Request.ext : %s", err.Error()) - } - bidAdjustmentFactors = requestExt.Prebid.BidAdjustmentFactors - if requestExt.Prebid.Cache != nil { - shouldCacheBids = requestExt.Prebid.Cache.Bids != nil - shouldCacheVAST = requestExt.Prebid.Cache.VastXML != nil - } - - if requestExt.Prebid.Targeting != nil { - targData = &targetData{ - priceGranularity: requestExt.Prebid.Targeting.PriceGranularity, - includeWinners: requestExt.Prebid.Targeting.IncludeWinners, - includeBidderKeys: requestExt.Prebid.Targeting.IncludeBidderKeys, - includeCacheBids: shouldCacheBids, - includeCacheVast: shouldCacheVAST, - includeFormat: requestExt.Prebid.Targeting.IncludeFormat, - } - targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() - } - } - // If we need to cache bids, then it will take some time to call prebid cache. // We should reduce the amount of time the bidders have, to compensate. - auctionCtx, cancel := e.makeAuctionContext(ctx, shouldCacheBids) //Why no context for `shouldCacheVast`? + auctionCtx, cancel := e.makeAuctionContext(ctx, shouldCacheBids) defer cancel() // Get currency rates conversions for the auction @@ -180,7 +167,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } if debugLog != nil && debugLog.Enabled { - bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errs) + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, debugInfo, errs) if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { debugLog.Data.Response = string(bidRespExtBytes) } else { @@ -205,7 +192,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } // Build the response - return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, bidResponseExt, errs) + return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, adapterExtra, auc, bidResponseExt, errs) } type DealTierInfo struct { @@ -284,6 +271,11 @@ func updateHbPbCatDur(bid *pbsOrtbBid, dealTierInfo *DealTierInfo, bidCategory m } } +func (e *exchange) makeDebugContext(ctx context.Context, debugInfo bool) (debugCtx context.Context) { + debugCtx = context.WithValue(ctx, DebugContextKey, debugInfo) + return +} + func (e *exchange) makeAuctionContext(ctx context.Context, needsCache bool) (auctionCtx context.Context, cancel context.CancelFunc) { auctionCtx = ctx cancel = func() {} @@ -445,7 +437,7 @@ func errsToBidderErrors(errs []error) []openrtb_ext.ExtBidderError { } // This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester -func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb.BidRequest, resolvedRequest json.RawMessage, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, errList []error) (*openrtb.BidResponse, error) { +func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, bidRequest *openrtb.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, errList []error) (*openrtb.BidResponse, error) { bidResponse := new(openrtb.BidResponse) bidResponse.ID = bidRequest.ID @@ -469,7 +461,12 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ bidResponse.SeatBid = seatBids if bidResponseExt == nil { - bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, resolvedRequest, errList) + contextDebugValue := ctx.Value(DebugContextKey) + var debugInfo bool + if contextDebugValue != nil { + debugInfo = contextDebugValue.(bool) + } + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, debugInfo, errList) } buffer := &bytes.Buffer{} enc := json.NewEncoder(buffer) @@ -480,7 +477,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ return bidResponse, err } -func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest, seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, categoriesFetcher stored_requests.CategoryFetcher, targData *targetData) (map[string]string, map[openrtb_ext.BidderName]*pbsOrtbSeatBid, []string, error) { +func applyCategoryMapping(ctx context.Context, requestExt *openrtb_ext.ExtRequest, seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, categoriesFetcher stored_requests.CategoryFetcher, targData *targetData) (map[string]string, map[openrtb_ext.BidderName]*pbsOrtbSeatBid, []string, error) { res := make(map[string]string) type bidDedupe struct { @@ -491,6 +488,8 @@ func applyCategoryMapping(ctx context.Context, requestExt openrtb_ext.ExtRequest dedupe := make(map[string]bidDedupe) + // applyCategoryMapping doesn't get called unless + // requestExt.Prebid.Targeting != nil && requestExt.Prebid.Targeting.IncludeBrandCategory != nil brandCatExt := requestExt.Prebid.Targeting.IncludeBrandCategory //If ext.prebid.targeting.includebrandcategory is present in ext then competitive exclusion feature is on. @@ -656,24 +655,22 @@ func getPrimaryAdServer(adServerId int) (string, error) { } // Extract all the data from the SeatBids and build the ExtBidResponse -func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, req *openrtb.BidRequest, resolvedRequest json.RawMessage, errList []error) *openrtb_ext.ExtBidResponse { +func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, req *openrtb.BidRequest, debugInfo bool, errList []error) *openrtb_ext.ExtBidResponse { bidResponseExt := &openrtb_ext.ExtBidResponse{ Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderError, len(adapterBids)), ResponseTimeMillis: make(map[openrtb_ext.BidderName]int, len(adapterBids)), RequestTimeoutMillis: req.TMax, } - if req.Test == 1 { + if debugInfo { bidResponseExt.Debug = &openrtb_ext.ExtResponseDebug{ - HttpCalls: make(map[openrtb_ext.BidderName][]*openrtb_ext.ExtHttpCall), - } - if err := json.Unmarshal(resolvedRequest, &bidResponseExt.Debug.ResolvedRequest); err != nil { - glog.Errorf("Error unmarshalling bid request snapshot: %v", err) + HttpCalls: make(map[openrtb_ext.BidderName][]*openrtb_ext.ExtHttpCall), + ResolvedRequest: req, } } for bidderName, responseExtra := range adapterExtra { - if req.Test == 1 { + if debugInfo { bidResponseExt.Debug.HttpCalls[bidderName] = responseExtra.HttpCalls } // Only make an entry for bidder errors if the bidder reported any. @@ -774,14 +771,6 @@ func (e *exchange) getBidCacheInfo(bid *pbsOrtbBid, auc *auction) (openrtb_ext.E return cacheInfo, found } -// Returns a snapshot of resolved bid request for debug if test field is set in the incomming request -func buildResolvedRequest(bidRequest *openrtb.BidRequest) (json.RawMessage, error) { - if bidRequest.Test == 1 { - return json.Marshal(bidRequest) - } - return nil, nil -} - func listBiddersWithRequests(cleanRequests map[openrtb_ext.BidderName]*openrtb.BidRequest) []openrtb_ext.BidderName { liveAdapters := make([]openrtb_ext.BidderName, len(cleanRequests)) i := 0 diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 96f740de23a..7da7b62e70b 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -22,6 +22,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" metricsConf "github.com/prebid/prebid-server/pbsmetrics/config" + metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config" pbc "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" @@ -112,9 +113,6 @@ func TestCharacterEscape(t *testing.T) { Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`), } - //resolvedRequest json.RawMessage - resolvedRequest := json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`) - //adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, adapterExtra := make(map[openrtb_ext.BidderName]*seatResponseExtra, 1) adapterExtra["appnexus"] = &seatResponseExtra{ @@ -126,7 +124,7 @@ func TestCharacterEscape(t *testing.T) { var errList []error /* 4) Build bid response */ - bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, nil, errList) + bidResp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, errList) /* 5) Assert we have no errors and one '&' character as we are supposed to */ if err != nil { @@ -140,6 +138,137 @@ func TestCharacterEscape(t *testing.T) { } } +// TestDebugBehaviour asserts the HttpCalls object is included inside the json "debug" field of the bidResponse extension when the +// openrtb.BidRequest "Test" value is set to 1 or the openrtb.BidRequest.Ext.Debug boolean field is set to true +func TestDebugBehaviour(t *testing.T) { + + // Define test cases + type inTest struct { + test int8 + debug bool + } + type outTest struct { + debugInfoIncluded bool + } + type aTest struct { + desc string + in inTest + out outTest + } + testCases := []aTest{ + { + desc: "test flag equals zero, ext debug flag false, no debug info expected", + in: inTest{test: 0, debug: false}, + out: outTest{debugInfoIncluded: false}, + }, + { + desc: "test flag equals zero, ext debug flag true, debug info expected", + in: inTest{test: 0, debug: true}, + out: outTest{debugInfoIncluded: true}, + }, + { + desc: "test flag equals 1, ext debug flag false, debug info expected", + in: inTest{test: 1, debug: false}, + out: outTest{debugInfoIncluded: true}, + }, + { + desc: "test flag equals 1, ext debug flag true, debug info expected", + in: inTest{test: 1, debug: true}, + out: outTest{debugInfoIncluded: true}, + }, + { + desc: "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected", + in: inTest{test: 2, debug: false}, + out: outTest{debugInfoIncluded: false}, + }, + { + desc: "test flag not equal to 0 nor 1, ext debug flag true, debug info expected", + in: inTest{test: -1, debug: true}, + out: outTest{debugInfoIncluded: true}, + }, + } + + // Set up test + noBidServer := func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + } + server := httptest.NewServer(http.HandlerFunc(noBidServer)) + defer server.Close() + + categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") + if error != nil { + t.Errorf("Failed to create a category Fetcher: %v", error) + } + + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{{ + ID: "some-impression-id", + Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, + Ext: json.RawMessage(`{"appnexus": {"placementId": 1}}`), + }}, + Site: &openrtb.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, + Device: &openrtb.Device{UA: "curl/7.54.0", IP: "::1"}, + AT: 1, + TMax: 500, + } + + bidderImpl := &goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + Body: []byte("{\"key\":\"val\"}"), + Headers: http.Header{}, + }, + bidResponse: &adapters.BidderResponse{}, + } + + e := new(exchange) + e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ + openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus), + } + e.cache = &wellBehavedCache{} + e.me = &metricsConf.DummyMetricsEngine{} + e.gDPR = gdpr.AlwaysAllow{} + e.currencyConverter = currencies.NewRateConverterDefault() + + // Run tests + for _, test := range testCases { + bidRequest.Test = test.in.test + + if test.in.debug { + bidRequest.Ext = json.RawMessage(`{"prebid":{"debug":true}}`) + } else { + bidRequest.Ext = nil + } + + // Run test + outBidResponse, err := e.HoldAuction(context.Background(), bidRequest, &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) + + // Assert no HoldAuction error + assert.NoErrorf(t, err, "%s. ex.HoldAuction returned an error: %v \n", test.desc, err) + assert.NotNilf(t, outBidResponse.Ext, "%s. outBidResponse.Ext should not be nil \n", test.desc) + + actualExt := &openrtb_ext.ExtBidResponse{} + err = json.Unmarshal(outBidResponse.Ext, actualExt) + assert.NoErrorf(t, err, "%s. \"ext\" JSON field could not be unmarshaled. err: \"%v\" \n outBidResponse.Ext: \"%s\" \n", test.desc, err, outBidResponse.Ext) + + if test.out.debugInfoIncluded { + assert.NotNilf(t, actualExt, "%s. ext.debug field is expected to be included in this outBidResponse.Ext and not be nil. outBidResponse.Ext.Debug = %v \n", test.desc, actualExt.Debug) + + // Assert "Debug fields + assert.Greater(t, len(actualExt.Debug.HttpCalls), 0, "%s. ext.debug.httpcalls array should not be empty\n", test.desc) + assert.Equal(t, server.URL, actualExt.Debug.HttpCalls["appnexus"][0].Uri, "%s. ext.debug.httpcalls array should not be empty\n", test.desc) + assert.NotNilf(t, actualExt.Debug.ResolvedRequest, "%s. ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil. outBidResponse.Ext.Debug = %v \n", test.desc, actualExt.Debug) + + // If not nil, assert bid extension + if test.in.debug { + diffJson(t, test.desc, bidRequest.Ext, actualExt.Debug.ResolvedRequest.Ext) + } + } + } +} + func TestGetBidCacheInfo(t *testing.T) { testUUID := "CACHE_UUID_1234" testExternalCacheHost := "https://www.externalprebidcache.net" @@ -230,9 +359,6 @@ func TestGetBidCacheInfo(t *testing.T) { }, } - //resolvedRequest json.RawMessage - resolvedRequest := json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`) - //adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, adapterExtra := map[openrtb_ext.BidderName]*seatResponseExtra{ bidderName: { @@ -278,7 +404,7 @@ func TestGetBidCacheInfo(t *testing.T) { var errList []error /* 4) Build bid response */ - bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, resolvedRequest, adapterExtra, auc, nil, errList) + bid_resp, err := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, errList) /* 5) Assert we have no errors and the bid response we expected*/ assert.NoError(t, err, "[TestGetBidCacheInfo] buildBidResponse() threw an error") @@ -342,8 +468,6 @@ func TestBidResponseCurrency(t *testing.T) { Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 10433394}}}],"tmax": 500}`), } - resolvedRequest := json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`) - adapterExtra := map[openrtb_ext.BidderName]*seatResponseExtra{ "appnexus": {ResponseTimeMillis: 5}, } @@ -449,7 +573,7 @@ func TestBidResponseCurrency(t *testing.T) { // Run tests for i := range testCases { - actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, nil, errList) + actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, nil, errList) assert.NoError(t, err, fmt.Sprintf("[TEST_FAILED] e.buildBidResponse resturns error in test: %s Error message: %s \n", testCases[i].description, err)) assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext)) } @@ -702,6 +826,38 @@ func TestTimeoutComputation(t *testing.T) { } } +func TestSetDebugContextKey(t *testing.T) { + // Test cases + testCases := []struct { + desc string + inDebugInfo bool + expectedDebugInfo bool + }{ + { + desc: "debugInfo flag on, we expect to find DebugContextKey key in context", + inDebugInfo: true, + expectedDebugInfo: true, + }, + { + desc: "debugInfo flag off, we don't expect to find DebugContextKey key in context", + inDebugInfo: false, + expectedDebugInfo: false, + }, + } + + // Setup test + ex := exchange{} + + // Run tests + for _, test := range testCases { + auctionCtx := ex.makeDebugContext(context.Background(), test.inDebugInfo) + + debugInfo := auctionCtx.Value(DebugContextKey) + assert.NotNil(t, debugInfo, "%s. Flag set, `debugInfo` shouldn't be nil") + assert.Equal(t, test.expectedDebugInfo, debugInfo.(bool), "Desc: %s. Incorrect value mapped to DebugContextKey(`debugInfo`) in the context\n", test.desc) + } +} + // TestExchangeJSON executes tests for all the *.json files in exchangetest. func TestExchangeJSON(t *testing.T) { if specFiles, err := ioutil.ReadDir("./exchangetest"); err == nil { @@ -974,7 +1130,7 @@ func TestCategoryMapping(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") assert.Equal(t, 1, len(rejections), "There should be 1 bid rejection message") @@ -1029,7 +1185,7 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") assert.Empty(t, rejections, "There should be no bid rejection messages") @@ -1081,7 +1237,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") assert.Equal(t, 1, len(rejections), "There should be 1 bid rejection message") @@ -1163,7 +1319,7 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") assert.Empty(t, rejections, "There should be no bid rejection messages") @@ -1229,7 +1385,7 @@ func TestCategoryDedupe(t *testing.T) { adapterBids[bidderName1] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, requestExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") assert.Equal(t, 2, len(rejections), "There should be 2 bid rejection messages") @@ -1340,7 +1496,7 @@ func TestBidRejectionErrors(t *testing.T) { adapterBids[bidderName] = &seatBid - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, test.reqExt, adapterBids, categoriesFetcher, targData) + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &test.reqExt, adapterBids, categoriesFetcher, targData) if len(test.expectedCatDur) > 0 { // Bid deduplication case diff --git a/exchange/utils.go b/exchange/utils.go index bc1b555e507..97fae7b78ca 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -22,12 +22,8 @@ import ( func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext.ExtRequestPrebidSChainSChain, error) { bidderToSChains := make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) - if len(req.Prebid.SChains) == 0 { - return bidderToSChains, nil - } - - for _, schainWrapper := range req.Prebid.SChains { - if schainWrapper != nil && len(schainWrapper.Bidders) > 0 { + if req != nil { + for _, schainWrapper := range req.Prebid.SChains { for _, bidder := range schainWrapper.Bidders { if _, present := bidderToSChains[bidder]; present { return nil, fmt.Errorf("request.ext.prebid.schains contains multiple schains for bidder %s; "+ @@ -49,6 +45,7 @@ func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext // 3. BidRequest.User.BuyerUID will be set to that Bidder's ID. func cleanOpenRTBRequests(ctx context.Context, orig *openrtb.BidRequest, + requestExt *openrtb_ext.ExtRequest, usersyncs IdFetcher, blables map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels, labels pbsmetrics.Labels, @@ -66,7 +63,7 @@ func cleanOpenRTBRequests(ctx context.Context, return } - requestsByBidder, errs = splitBidRequest(orig, impsByBidder, aliases, usersyncs, blables, labels) + requestsByBidder, errs = splitBidRequest(orig, requestExt, impsByBidder, aliases, usersyncs, blables, labels) gdpr := extractGDPR(orig, usersyncIfAmbiguous) consent := extractConsent(orig) @@ -125,6 +122,7 @@ func cleanOpenRTBRequests(ctx context.Context, } func splitBidRequest(req *openrtb.BidRequest, + requestExt *openrtb_ext.ExtRequest, impsByBidder map[string][]openrtb.Imp, aliases map[string]string, usersyncs IdFetcher, @@ -137,20 +135,16 @@ func splitBidRequest(req *openrtb.BidRequest, return nil, []error{err} } - var requestExt openrtb_ext.ExtRequest var sChainsByBidder map[string]*openrtb_ext.ExtRequestPrebidSChainSChain - if len(req.Ext) > 0 { - err := json.Unmarshal(req.Ext, &requestExt) - if err != nil { - return nil, []error{err} - } - sChainsByBidder, err = BidderToPrebidSChains(&requestExt) - if err != nil { - return nil, []error{err} - } - } else { - sChainsByBidder = make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) + sChainsByBidder, err = BidderToPrebidSChains(requestExt) + if err != nil { + return nil, []error{err} + } + + reqExt, err := getExtJson(req, requestExt) + if err != nil { + return nil, []error{err} } for bidder, imps := range impsByBidder { @@ -174,23 +168,21 @@ func splitBidRequest(req *openrtb.BidRequest, reqCopy.Imp = imps prepareSource(&reqCopy, bidder, sChainsByBidder) - prepareExt(&reqCopy, &requestExt) + reqCopy.Ext = reqExt requestsByBidder[openrtb_ext.BidderName(bidder)] = &reqCopy } return requestsByBidder, nil } -func prepareExt(req *openrtb.BidRequest, unpackedExt *openrtb_ext.ExtRequest) { - if len(req.Ext) == 0 { - return +func getExtJson(req *openrtb.BidRequest, unpackedExt *openrtb_ext.ExtRequest) (json.RawMessage, error) { + if len(req.Ext) == 0 || unpackedExt == nil { + return json.RawMessage(``), nil } + extCopy := *unpackedExt extCopy.Prebid.SChains = nil - reqExt, err := json.Marshal(extCopy) - if err == nil { - req.Ext = reqExt - } + return json.Marshal(extCopy) } func prepareSource(req *openrtb.BidRequest, bidder string, sChainsByBidder map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) { @@ -434,3 +426,55 @@ func randomizeList(list []openrtb_ext.BidderName) { list[i], list[j] = list[j], list[i] } } + +func extractBidRequestExt(bidRequest *openrtb.BidRequest) (*openrtb_ext.ExtRequest, error) { + requestExt := &openrtb_ext.ExtRequest{} + + if bidRequest == nil { + return requestExt, fmt.Errorf("Error bidRequest should not be nil") + } + + if len(bidRequest.Ext) > 0 { + err := json.Unmarshal(bidRequest.Ext, &requestExt) + if err != nil { + return requestExt, fmt.Errorf("Error decoding Request.ext : %s", err.Error()) + } + } + return requestExt, nil +} + +func getExtCacheInfo(requestExt *openrtb_ext.ExtRequest) (shouldCacheBids bool, shouldCacheVAST bool) { + if requestExt != nil && requestExt.Prebid.Cache != nil { + shouldCacheBids = requestExt.Prebid.Cache.Bids != nil + shouldCacheVAST = requestExt.Prebid.Cache.VastXML != nil + } + return +} + +func getExtTargetData(requestExt *openrtb_ext.ExtRequest, shouldCacheBids bool, shouldCacheVAST bool) *targetData { + var targData *targetData + + if requestExt != nil && requestExt.Prebid.Targeting != nil { + targData = &targetData{ + priceGranularity: requestExt.Prebid.Targeting.PriceGranularity, + includeWinners: requestExt.Prebid.Targeting.IncludeWinners, + includeBidderKeys: requestExt.Prebid.Targeting.IncludeBidderKeys, + includeCacheBids: shouldCacheBids, + includeCacheVast: shouldCacheVAST, + includeFormat: requestExt.Prebid.Targeting.IncludeFormat, + } + } + return targData +} + +func getDebugInfo(bidRequest *openrtb.BidRequest, requestExt *openrtb_ext.ExtRequest) bool { + return (bidRequest != nil && bidRequest.Test == 1) || (requestExt != nil && requestExt.Prebid.Debug) +} + +func getExtBidAdjustmentFactors(requestExt *openrtb_ext.ExtRequest) map[string]float64 { + var bidAdjustmentFactors map[string]float64 + if requestExt != nil { + bidAdjustmentFactors = requestExt.Prebid.BidAdjustmentFactors + } + return bidAdjustmentFactors +} diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 608e6a17a10..3b919d3da56 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -3,6 +3,7 @@ package exchange import ( "context" "encoding/json" + "fmt" "testing" "github.com/mxmCherry/openrtb" @@ -79,7 +80,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { } for _, test := range testCases { - reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -141,7 +142,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) @@ -185,7 +186,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { req := newBidRequest(t) req.Regs = &openrtb.Regs{COPPA: test.coppa} - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}) result := results["appnexus"] assert.Nil(t, errs) @@ -202,77 +203,92 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { func TestCleanOpenRTBRequestsSChain(t *testing.T) { testCases := []struct { - description string - inSourceExt json.RawMessage - inExt json.RawMessage - outSourceExt json.RawMessage - outExt json.RawMessage - hasError bool + description string + inExt json.RawMessage + inSourceExt json.RawMessage + outSourceExt json.RawMessage + outRequestExt json.RawMessage + hasError bool }{ { - description: "Empty root ext and source ext", - inSourceExt: json.RawMessage(``), - inExt: json.RawMessage(``), - outSourceExt: json.RawMessage(``), - outExt: json.RawMessage(``), - hasError: false, + description: "Empty root ext and source ext, nil unmarshaled ext", + inExt: nil, + inSourceExt: json.RawMessage(``), + outSourceExt: json.RawMessage(``), + outRequestExt: json.RawMessage(``), + hasError: false, }, { - description: "No schains in root ext and empty source ext", - inSourceExt: json.RawMessage(``), - inExt: json.RawMessage(`{"prebid":{"schains":[]}}`), - outSourceExt: json.RawMessage(``), - outExt: json.RawMessage(`{"prebid":{}}`), - hasError: false, + description: "Empty root ext, source ext, and unmarshaled ext", + inExt: json.RawMessage(``), + inSourceExt: json.RawMessage(``), + outSourceExt: json.RawMessage(``), + outRequestExt: json.RawMessage(``), + hasError: false, }, { - description: "Use source schain -- no bidder schain or wildcard schain in ext.prebid.schains", - inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), - inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["bidder1"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), - outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), - outExt: json.RawMessage(`{"prebid":{}}`), - hasError: false, + description: "No schains in root ext and empty source ext. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[]}}`), + outSourceExt: json.RawMessage(``), + outRequestExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, }, { - description: "Use schain for bidder in ext.prebid.schains", - inSourceExt: json.RawMessage(``), - inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), - outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), - outExt: json.RawMessage(`{"prebid":{}}`), - hasError: false, + description: "Use source schain -- no bidder schain or wildcard schain in ext.prebid.schains. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["bidder1"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + outRequestExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, }, { - description: "Use wildcard schain in ext.prebid.schains", - inSourceExt: json.RawMessage(``), - inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), - outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), - outExt: json.RawMessage(`{"prebid":{}}`), - hasError: false, + description: "Use schain for bidder in ext.prebid.schains. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outRequestExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, }, { - description: "Use schain for bidder in ext.prebid.schains instead of wildcard", - inSourceExt: json.RawMessage(``), - inExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"},"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"wildcard.com","sid":"wildcard1","rid":"WildcardReq1","hp":1}],"ver":"1.0"}} ]}}`), - outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), - outExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"}}}`), - hasError: false, + description: "Use wildcard schain in ext.prebid.schains. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outRequestExt: json.RawMessage(`{"prebid":{}}`), + hasError: false, }, { - description: "Use source schain -- multiple (two) bidder schains in ext.prebid.schains", - inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), - inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), - outSourceExt: nil, - outExt: nil, - hasError: true, + description: "Use schain for bidder in ext.prebid.schains instead of wildcard. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(``), + inExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"},"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["*"],"schain":{"complete":1,"nodes":[{"asi":"wildcard.com","sid":"wildcard1","rid":"WildcardReq1","hp":1}],"ver":"1.0"}} ]}}`), + outSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`), + outRequestExt: json.RawMessage(`{"prebid":{"aliases":{"appnexus":"alias1"}}}`), + hasError: false, + }, + { + description: "Use source schain -- multiple (two) bidder schains in ext.prebid.schains. Unmarshaled ext is equivalent to root ext", + inSourceExt: json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"example.com","sid":"example1","rid":"ExampleReq1","hp":1}],"ver":"1.0"}}`), + inExt: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), + outSourceExt: nil, + outRequestExt: nil, + hasError: true, }, } for _, test := range testCases { req := newBidRequest(t) req.Source.Ext = test.inSourceExt - req.Ext = test.inExt - results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, config.Privacy{}) + var extRequest *openrtb_ext.ExtRequest + if test.inExt != nil { + req.Ext = test.inExt + unmarshaledExt, err := extractBidRequestExt(req) + assert.NoErrorf(t, err, test.description+":Error unmarshaling inExt") + extRequest = unmarshaledExt + } + + results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, extRequest, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, config.Privacy{}) result := results["appnexus"] if test.hasError == true { @@ -281,11 +297,295 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { } else { assert.Nil(t, errs) assert.Equal(t, test.outSourceExt, result.Source.Ext, test.description+":Source.Ext") - assert.Equal(t, test.outExt, result.Ext, test.description+":Ext") + assert.Equal(t, test.outRequestExt, result.Ext, test.description+":Ext") } } } +func TestExtractBidRequesteExt(t *testing.T) { + testCases := []struct { + desc string + inBidRequest *openrtb.BidRequest + outRequestExt *openrtb_ext.ExtRequest + outError error + }{ + { + desc: "Valid", + inBidRequest: &openrtb.BidRequest{Ext: json.RawMessage(`{"prebid":{"debug":true}}`)}, + outRequestExt: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: true}}, + outError: nil, + }, + { + desc: "bidRequest nil, we expect an error", + inBidRequest: nil, + outRequestExt: &openrtb_ext.ExtRequest{}, + outError: fmt.Errorf("Error bidRequest should not be nil"), + }, + { + desc: "Non-nil bidRequest with empty Ext, we expect a blank requestExt", + inBidRequest: &openrtb.BidRequest{}, + outRequestExt: &openrtb_ext.ExtRequest{}, + outError: nil, + }, + { + desc: "Non-nil bidRequest with non-empty, invalid Ext, we expect unmarshaling error", + inBidRequest: &openrtb.BidRequest{Ext: json.RawMessage(`invalid`)}, + outRequestExt: &openrtb_ext.ExtRequest{}, + outError: fmt.Errorf("Error decoding Request.ext : invalid character 'i' looking for beginning of value"), + }, + } + for _, test := range testCases { + actualRequestExt, actualErr := extractBidRequestExt(test.inBidRequest) + + assert.Equal(t, test.outRequestExt, actualRequestExt, "%s. Unexpected RequestExt value. \n", test.desc) + assert.Equal(t, test.outError, actualErr, "%s. Unexpected error value. \n", test.desc) + } +} + +func TestGetExtCacheInfo(t *testing.T) { + testCases := []struct { + desc string + inRequestExt *openrtb_ext.ExtRequest + outCacheBids bool + outCacheVAST bool + }{ + { + desc: "Nil inRequestExt, both cache flags false", + inRequestExt: nil, + outCacheBids: false, + outCacheVAST: false, + }, + { + desc: "Non-nil inRequestExt, nil Cache info, both cache flags false", + inRequestExt: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Cache: nil}}, + outCacheBids: false, + outCacheVAST: false, + }, + { + desc: "Non-nil inRequestExt, non-nil Cache info, both ExtRequestPrebidCacheBids and ExtRequestPrebidCacheVAST nil", + inRequestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Cache: &openrtb_ext.ExtRequestPrebidCache{ + Bids: nil, + VastXML: nil, + }, + }, + }, + outCacheBids: false, + outCacheVAST: false, + }, + { + desc: "Non-nil inRequestExt, non-nil Cache info, both ExtRequestPrebidCacheBids nil, shouldCacheVast true", + inRequestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Cache: &openrtb_ext.ExtRequestPrebidCache{ + Bids: nil, + VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{}, + }, + }, + }, + outCacheBids: false, + outCacheVAST: true, + }, + { + desc: "Non-nil inRequestExt, non-nil Cache info, both ExtRequestPrebidCacheVAST nil, shouldCacheBids true", + inRequestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Cache: &openrtb_ext.ExtRequestPrebidCache{ + Bids: &openrtb_ext.ExtRequestPrebidCacheBids{}, + VastXML: nil, + }, + }, + }, + outCacheBids: true, + outCacheVAST: false, + }, + { + desc: "Non-nil inRequestExt, non-nil Cache info values, both shouldCacheBids and shouldCacheVAST return true", + inRequestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Cache: &openrtb_ext.ExtRequestPrebidCache{ + Bids: &openrtb_ext.ExtRequestPrebidCacheBids{}, + VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{}, + }, + }, + }, + outCacheBids: true, + outCacheVAST: true, + }, + } + for _, test := range testCases { + shouldCacheBids, shouldCacheVAST := getExtCacheInfo(test.inRequestExt) + + assert.Equal(t, test.outCacheBids, shouldCacheBids, "%s. Unexpected shouldCacheBids value. \n", test.desc) + assert.Equal(t, test.outCacheVAST, shouldCacheVAST, "%s. Unexpected shouldCacheVAST value. \n", test.desc) + } +} + +func TestGetExtTargetData(t *testing.T) { + type inTest struct { + requestExt *openrtb_ext.ExtRequest + shouldCacheBids bool + shouldCacheVAST bool + } + type outTest struct { + targetData *targetData + nilTargetData bool + } + testCases := []struct { + desc string + in inTest + out outTest + }{ + { + "nil requestExt, nil outTargetData", + inTest{ + requestExt: nil, + shouldCacheBids: true, + shouldCacheVAST: true, + }, + outTest{targetData: nil, nilTargetData: true}, + }, + { + "Valid requestExt, nil Targeting field, nil outTargetData", + inTest{ + requestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Targeting: nil, + }, + }, + shouldCacheBids: true, + shouldCacheVAST: true, + }, + outTest{targetData: nil, nilTargetData: true}, + }, + { + "Valid targeting data in requestExt, valid outTargetData", + inTest{ + requestExt: &openrtb_ext.ExtRequest{ + Prebid: openrtb_ext.ExtRequestPrebid{ + Targeting: &openrtb_ext.ExtRequestTargeting{ + PriceGranularity: openrtb_ext.PriceGranularity{ + Precision: 2, + Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, + }, + IncludeWinners: true, + IncludeBidderKeys: true, + }, + }, + }, + shouldCacheBids: true, + shouldCacheVAST: true, + }, + outTest{ + targetData: &targetData{ + priceGranularity: openrtb_ext.PriceGranularity{ + Precision: 2, + Ranges: []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}}, + }, + includeWinners: true, + includeBidderKeys: true, + includeCacheBids: true, + includeCacheVast: true, + }, + nilTargetData: false, + }, + }, + } + for _, test := range testCases { + actualTargetData := getExtTargetData(test.in.requestExt, test.in.shouldCacheBids, test.in.shouldCacheVAST) + + if test.out.nilTargetData { + assert.Nil(t, actualTargetData, "%s. Targeting data should be nil. \n", test.desc) + } else { + assert.NotNil(t, actualTargetData, "%s. Targeting data should NOT be nil. \n", test.desc) + assert.Equal(t, *test.out.targetData, *actualTargetData, "%s. Unexpected targeting data value. \n", test.desc) + } + } +} + +func TestGetDebugInfo(t *testing.T) { + type inTest struct { + bidRequest *openrtb.BidRequest + requestExt *openrtb_ext.ExtRequest + } + testCases := []struct { + desc string + in inTest + out bool + }{ + { + desc: "Nil bid request, nil requestExt", + in: inTest{nil, nil}, + out: false, + }, + { + desc: "bid request test == 0, nil requestExt", + in: inTest{&openrtb.BidRequest{Test: 0}, nil}, + out: false, + }, + { + desc: "Nil bid request, requestExt debug flag false", + in: inTest{nil, &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: false}}}, + out: false, + }, + { + desc: "bid request test == 0, requestExt debug flag false", + in: inTest{&openrtb.BidRequest{Test: 0}, &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: false}}}, + out: false, + }, + { + desc: "bid request test == 1, requestExt debug flag false", + in: inTest{&openrtb.BidRequest{Test: 1}, &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: false}}}, + out: true, + }, + { + desc: "bid request test == 0, requestExt debug flag true", + in: inTest{&openrtb.BidRequest{Test: 0}, &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: true}}}, + out: true, + }, + { + desc: "bid request test == 1, requestExt debug flag true", + in: inTest{&openrtb.BidRequest{Test: 1}, &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Debug: true}}}, + out: true, + }, + } + for _, test := range testCases { + actualDebugInfo := getDebugInfo(test.in.bidRequest, test.in.requestExt) + + assert.Equal(t, test.out, actualDebugInfo, "%s. Unexpected debug value. \n", test.desc) + } +} + +func TestGetExtBidAdjustmentFactors(t *testing.T) { + testCases := []struct { + desc string + inRequestExt *openrtb_ext.ExtRequest + outBidAdjustmentFactors map[string]float64 + }{ + { + desc: "Nil request ext", + inRequestExt: nil, + outBidAdjustmentFactors: nil, + }, + { + desc: "Non-nil request ext, nil BidAdjustmentFactors field", + inRequestExt: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: nil}}, + outBidAdjustmentFactors: nil, + }, + { + desc: "Non-nil request ext, valid BidAdjustmentFactors field", + inRequestExt: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: map[string]float64{"bid-factor": 1.0}}}, + outBidAdjustmentFactors: map[string]float64{"bid-factor": 1.0}, + }, + } + for _, test := range testCases { + actualBidAdjustmentFactors := getExtBidAdjustmentFactors(test.inRequestExt) + + assert.Equal(t, test.outBidAdjustmentFactors, actualBidAdjustmentFactors, "%s. Unexpected BidAdjustmentFactors value. \n", test.desc) + } +} + func TestCleanOpenRTBRequestsLMT(t *testing.T) { var ( enabled int8 = 1 @@ -346,7 +646,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) @@ -427,7 +727,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: !test.gdprScrub}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: !test.gdprScrub}, true, privacyConfig) result := results["appnexus"] assert.Nil(t, errs) diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index acfd4a1e71f..23daaf0f76e 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -19,6 +19,7 @@ type ExtRequestPrebid struct { StoredRequest *ExtStoredRequest `json:"storedrequest,omitempty"` Targeting *ExtRequestTargeting `json:"targeting,omitempty"` SupportDeals bool `json:"supportdeals,omitempty"` + Debug bool `json:"debug,omitempty"` } // ExtRequestPrebid defines the contract for bidrequest.ext.prebid.schains From cc4350270973749b5cbcc4f5bd191f4daeb13dbe Mon Sep 17 00:00:00 2001 From: Adprime <64427228+Adprime@users.noreply.github.com> Date: Fri, 7 Aug 2020 18:06:46 +0300 Subject: [PATCH 159/318] Remove redundad struct (#1432) --- adapters/adprime/adprime.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go index 007d3c86570..8594cb5d2e4 100644 --- a/adapters/adprime/adprime.go +++ b/adapters/adprime/adprime.go @@ -24,10 +24,6 @@ func NewAdprimeBidder(endpoint string) *AdprimeAdapter { } } -type adprimeParams struct { - TagID string `json:"TagID"` -} - // MakeRequests create bid request for adprime demand func (a *AdprimeAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var errs []error From e67dfa4b8b58f995bda215299e1a4435a3d0e59b Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 12 Aug 2020 10:14:36 -0400 Subject: [PATCH 160/318] Tcf2 id support (#1420) --- endpoints/auction_test.go | 5 +++-- endpoints/cookie_sync_test.go | 4 ++-- endpoints/setuid_test.go | 4 ++-- exchange/utils.go | 4 +++- exchange/utils_test.go | 4 ++-- gdpr/gdpr.go | 2 +- gdpr/impl.go | 35 ++++++++++++++++++------------ gdpr/impl_test.go | 35 +++++++++++++++++++++++------- privacy/enforcement.go | 5 +++-- privacy/enforcement_test.go | 40 +++++++++++++++++++++++++++++++---- 10 files changed, 100 insertions(+), 38 deletions(-) diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go index 5e9e9639a9c..028f119640a 100644 --- a/endpoints/auction_test.go +++ b/endpoints/auction_test.go @@ -408,6 +408,7 @@ type auctionMockPermissions struct { allowHostCookies bool allowPI bool allowGeo bool + allowID bool } func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { @@ -418,8 +419,8 @@ func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder o return m.allowBidderSync, nil } -func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - return m.allowPI, m.allowGeo, nil +func (m *auctionMockPermissions) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return m.allowPI, m.allowGeo, m.allowID, nil } func (m *auctionMockPermissions) AMPException() bool { diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 824e32f1957..f7974d2bc77 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -254,8 +254,8 @@ func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.Bi return ok, nil } -func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - return true, true, nil +func (g *gdprPerms) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return true, true, true, nil } func (g *gdprPerms) AMPException() bool { diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 3f47b257d2e..e63944e2aec 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -437,8 +437,8 @@ func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return false, nil } -func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - return g.allowPI, g.allowPI, nil +func (g *mockPermsSetUID) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return g.allowPI, g.allowPI, g.allowPI, nil } func (g *mockPermsSetUID) AMPException() bool { diff --git a/exchange/utils.go b/exchange/utils.go index 97fae7b78ca..2131aac5f41 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -107,12 +107,14 @@ func cleanOpenRTBRequests(ctx context.Context, coreBidder := resolveBidder(bidder.String(), aliases) var publisherID = labels.PubID - ok, geo, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) + ok, geo, id, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) privacyEnforcement.GDPR = !ok && err == nil privacyEnforcement.GDPRGeo = !geo && err == nil + privacyEnforcement.GDPRID = !id && err == nil } else { privacyEnforcement.GDPR = false privacyEnforcement.GDPRGeo = false + privacyEnforcement.GDPRID = false } privacyEnforcement.Apply(bidReq, ampGDPRException) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 3b919d3da56..528e875ab16 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -28,8 +28,8 @@ func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return true, nil } -func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - return p.personalInfoAllowed, p.personalInfoAllowed, nil +func (p *permissionsMock) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return p.personalInfoAllowed, p.personalInfoAllowed, p.personalInfoAllowed, nil } func (p *permissionsMock) AMPException() bool { diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index 0dfa12f5ebd..04db8cb92ed 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -23,7 +23,7 @@ type Permissions interface { // Determines whether or not to send PI information to a bidder, or mask it out. // // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. - PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) + PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) // Exposes the AMP execption flag AMPException() bool diff --git a/gdpr/impl.go b/gdpr/impl.go index 60db804aec6..2deddc7b2ba 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -42,10 +42,10 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ return false, nil } -func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { +func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { _, ok := p.cfg.NonStandardPublisherMap[PublisherID] if ok { - return true, true, nil + return true, true, true, nil } id, ok := p.vendorIDs[bidder] @@ -54,10 +54,10 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt } if consent == "" { - return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil + return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil } - return false, false, nil + return false, false, false, nil } func (p *permissionsImpl) AMPException() bool { @@ -98,19 +98,19 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consen return false, nil } -func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, bool, error) { +func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent string) (bool, bool, bool, error) { // If we're not given a consent string, respect the preferences in the app config. if consent == "" { - return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil + return p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, p.cfg.UsersyncIfAmbiguous, nil } parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent) if err != nil { - return false, false, err + return false, false, false, err } if vendor == nil { - return false, false, nil + return false, false, false, nil } if parsedConsent.Version() == 2 { @@ -118,21 +118,22 @@ func (p *permissionsImpl) allowPI(ctx context.Context, vendorID uint16, consent return p.allowPITCF2(parsedConsent, vendor, vendorID) } if (vendor.Purpose(consentconstants.InfoStorageAccess) || vendor.LegitimateInterest(consentconstants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(consentconstants.InfoStorageAccess) && (vendor.Purpose(consentconstants.PersonalizationProfile) || vendor.LegitimateInterest(consentconstants.PersonalizationProfile)) && parsedConsent.PurposeAllowed(consentconstants.PersonalizationProfile) && parsedConsent.VendorConsent(vendorID) { - return true, true, nil + return true, true, true, nil } } else { if (vendor.Purpose(tcf1constants.InfoStorageAccess) || vendor.LegitimateInterest(tcf1constants.InfoStorageAccess)) && parsedConsent.PurposeAllowed(tcf1constants.InfoStorageAccess) && (vendor.Purpose(tcf1constants.AdSelectionDeliveryReporting) || vendor.LegitimateInterest(tcf1constants.AdSelectionDeliveryReporting)) && parsedConsent.PurposeAllowed(tcf1constants.AdSelectionDeliveryReporting) && parsedConsent.VendorConsent(vendorID) { - return true, true, nil + return true, true, true, nil } } - return false, false, nil + return false, false, false, nil } -func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16) (allowPI bool, allowGeo bool, err error) { +func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor api.Vendor, vendorID uint16) (allowPI bool, allowGeo bool, allowID bool, err error) { consent, ok := parsedConsent.(tcf2.ConsentMetadata) err = nil allowPI = false allowGeo = false + allowID = false if !ok { err = fmt.Errorf("Unable to access TCF2 parsed consent") return @@ -142,6 +143,12 @@ func (p *permissionsImpl) allowPITCF2(parsedConsent api.VendorConsents, vendor a } else { allowGeo = true } + for i := 2; i <= 10; i++ { + if p.checkPurpose(consent, vendor, vendorID, tcf1constants.Purpose(i)) { + allowID = true + break + } + } // Set to true so any purpose check can flip it to false allowPI = true if p.cfg.TCF2.Purpose1.Enabled { @@ -214,8 +221,8 @@ func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.B return true, nil } -func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, error) { - return true, true, nil +func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return true, true, true, nil } func (a AlwaysAllow) AMPException() bool { diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index 05b2fb6d98e..053e87536ab 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -207,17 +207,17 @@ func TestAllowPersonalInfo(t *testing.T) { } // PI needs both purposes to succeed - allowPI, _, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, _, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, false, allowPI) - allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderPubmatic, "", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, true, allowPI) // Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1} - allowPI, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") + allowPI, _, _, err = perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "BOS2bx5OS2bx5ABABBAAABoAAAABBwAA") assertNilErr(t, err) assertBoolsEqual(t, true, allowPI) } @@ -257,6 +257,7 @@ type tcf2TestDef struct { consent string allowPI bool allowGeo bool + allowID bool } func TestAllowPersonalInfoTCF2(t *testing.T) { @@ -285,6 +286,7 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", allowPI: false, allowGeo: false, + allowID: false, }, { description: "Pubmatic vendor test, flex purposes claimed", @@ -292,6 +294,7 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", allowPI: true, allowGeo: true, + allowID: true, }, { description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", @@ -299,14 +302,16 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", allowPI: true, allowGeo: false, + allowID: true, }, } for _, td := range testDefs { - allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + assert.EqualValuesf(t, td.allowID, allowID, "AllowGeo failure on %s", td.description) } } @@ -328,10 +333,11 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) { } // Assert that an item that otherwise would not be allowed PI access, gets approved because it is found in the GDPR.NonStandardPublishers array perms.cfg.NonStandardPublisherMap = map[string]int{"appNexusAppID": 1} - allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), openrtb_ext.BidderAppnexus, "appNexusAppID", "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed") assert.EqualValuesf(t, true, allowPI, "AllowPI failure") assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure") + assert.EqualValuesf(t, true, allowID, "AllowID failure") } @@ -361,6 +367,7 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", allowPI: false, allowGeo: false, + allowID: false, }, { description: "Pubmatic vendor test, flex purposes claimed", @@ -368,6 +375,7 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", allowPI: false, allowGeo: false, + allowID: false, }, { description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", @@ -375,14 +383,16 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { consent: "COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA", allowPI: false, allowGeo: false, + allowID: true, }, } for _, td := range testDefs { - allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + assert.EqualValuesf(t, td.allowID, allowID, "AllowPI failure on %s", td.description) } } @@ -413,6 +423,7 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: false, allowGeo: false, + allowID: false, }, { description: "Pubmatic vendor test, flex purposes claimed", @@ -420,6 +431,7 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: true, allowGeo: true, + allowID: true, }, { description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", @@ -427,14 +439,16 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: true, allowGeo: false, + allowID: true, }, } for _, td := range testDefs { - allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description) } } @@ -458,6 +472,7 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { perms.cfg.TCF2.PurposeOneTreatment.AccessAllowed = false // COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA Purpose one flag set + // Purpose one treatment will fail PI, but allow passing the IDs. testDefs := []tcf2TestDef{ { description: "Appnexus vendor test, insufficient purposes claimed", @@ -465,6 +480,7 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: false, allowGeo: false, + allowID: false, }, { description: "Pubmatic vendor test, flex purposes claimed", @@ -472,6 +488,7 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: false, allowGeo: true, + allowID: true, }, { description: "Rubicon vendor test, Specific purposes/LIs claimed, no geo claimed", @@ -479,14 +496,16 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { consent: "COzqiL3OzqiL3NIAAAENAiCMAP_AAH_AAIAAAQEX2S5MAICL7JcmAAA", allowPI: false, allowGeo: false, + allowID: true, }, } for _, td := range testDefs { - allowPI, allowGeo, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) + allowPI, allowGeo, allowID, err := perms.PersonalInfoAllowed(context.Background(), td.bidder, "", td.consent) assert.NoErrorf(t, err, "Error processing PersonalInfoAllowed for %s", td.description) assert.EqualValuesf(t, td.allowPI, allowPI, "AllowPI failure on %s", td.description) assert.EqualValuesf(t, td.allowGeo, allowGeo, "AllowGeo failure on %s", td.description) + assert.EqualValuesf(t, td.allowID, allowID, "AllowID failure on %s", td.description) } } diff --git a/privacy/enforcement.go b/privacy/enforcement.go index 8a5d201fc95..9c23c320680 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -10,12 +10,13 @@ type Enforcement struct { COPPA bool GDPR bool GDPRGeo bool + GDPRID bool LMT bool } // Any returns true if at least one privacy policy requires enforcement. func (e Enforcement) Any() bool { - return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo || e.LMT + return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo || e.GDPRID || e.LMT } // Apply cleans personally identifiable information from an OpenRTB bid request. @@ -64,7 +65,7 @@ func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUs } // If no user scrubbing is needed, then return none, else scrub ID (COPPA checked above) - if e.CCPA || e.GDPR || e.LMT { + if e.CCPA || e.GDPRID || e.LMT { return ScrubStrategyUserID } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index 968c6354710..ef02e28147a 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -21,6 +21,7 @@ func TestAny(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: false, }, expected: false, @@ -32,6 +33,7 @@ func TestAny(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + GDPRID: true, LMT: true, }, expected: true, @@ -43,6 +45,7 @@ func TestAny(t *testing.T) { COPPA: true, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: true, }, expected: true, @@ -72,6 +75,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + GDPRID: true, LMT: true, }, ampGDPRException: false, @@ -87,6 +91,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: false, }, ampGDPRException: false, @@ -102,6 +107,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: false, }, ampGDPRException: false, @@ -117,6 +123,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: true, + GDPRID: true, LMT: false, }, ampGDPRException: false, @@ -132,6 +139,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: true, + GDPRID: true, LMT: false, }, ampGDPRException: true, @@ -147,6 +155,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: false, }, ampGDPRException: true, @@ -162,6 +171,7 @@ func TestApply(t *testing.T) { COPPA: true, GDPR: true, GDPRGeo: true, + GDPRID: true, LMT: false, }, ampGDPRException: true, @@ -177,6 +187,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: true, GDPRGeo: false, + GDPRID: true, LMT: false, }, ampGDPRException: false, @@ -192,6 +203,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: true, + GDPRID: false, LMT: false, }, ampGDPRException: false, @@ -200,6 +212,22 @@ func TestApply(t *testing.T) { expectedUser: ScrubStrategyUserNone, expectedUserGeo: ScrubStrategyGeoReducedPrecision, }, + { + description: "GDPR Only, ID exception", + enforcement: Enforcement{ + CCPA: false, + COPPA: false, + GDPR: true, + GDPRGeo: true, + GDPRID: false, + LMT: false, + }, + ampGDPRException: false, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedUser: ScrubStrategyUserNone, + expectedUserGeo: ScrubStrategyGeoReducedPrecision, + }, { description: "LMT Only", enforcement: Enforcement{ @@ -207,6 +235,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: true, }, ampGDPRException: false, @@ -222,6 +251,7 @@ func TestApply(t *testing.T) { COPPA: false, GDPR: false, GDPRGeo: false, + GDPRID: false, LMT: true, }, ampGDPRException: true, @@ -258,10 +288,12 @@ func TestApplyNoneApplicable(t *testing.T) { m := &mockScrubber{} enforcement := Enforcement{ - CCPA: false, - COPPA: false, - GDPR: false, - LMT: false, + CCPA: false, + COPPA: false, + GDPR: false, + GDPRGeo: false, + GDPRID: false, + LMT: false, } enforcement.apply(req, false, m) From 549cc791f7d06d1a3dfaa1be061b1753c4a8146a Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 12 Aug 2020 12:27:57 -0400 Subject: [PATCH 161/318] Default TCF1 GVL in anticipation of IAB no longer hosting the v1 GVL (#1433) --- config/config.go | 9 +++ gdpr/vendorlist-fetching.go | 33 ++++++++++- gdpr/vendorlist-fetching_test.go | 97 ++++++++++++++++++++++++++++++++ static/tcf1/fallback_gvl.json | 1 + 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 static/tcf1/fallback_gvl.json diff --git a/config/config.go b/config/config.go index 9663b021b5b..7fc77855810 100755 --- a/config/config.go +++ b/config/config.go @@ -156,6 +156,7 @@ type GDPR struct { Timeouts GDPRTimeouts `mapstructure:"timeouts_ms"` NonStandardPublishers []string `mapstructure:"non_standard_publishers,flow"` NonStandardPublisherMap map[string]int + TCF1 TCF1 `mapstructure:"tcf1"` TCF2 TCF2 `mapstructure:"tcf2"` AMPException bool `mapstructure:"amp_exception"` } @@ -180,6 +181,12 @@ func (t *GDPRTimeouts) ActiveTimeout() time.Duration { return time.Duration(t.ActiveVendorlistFetch) * time.Millisecond } +// TCF1 defines the TCF1 specific configurations for GDPR +type TCF1 struct { + FetchGVL bool `mapstructure:"fetch_gvl"` + FallbackGVLPath string `mapstructure:"fallback_gvl_path"` +} + // TCF2 defines the TCF2 specific configurations for GDPR type TCF2 struct { Enabled bool `mapstructure:"enabled"` @@ -885,6 +892,8 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0) v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0) v.SetDefault("gdpr.non_standard_publishers", []string{""}) + v.SetDefault("gdpr.tcf1.fetch_gvl", true) + v.SetDefault("gdpr.tcf1.fallback_gvl_path", "./static/tcf1/fallback_gvl.json") v.SetDefault("gdpr.tcf2.enabled", true) v.SetDefault("gdpr.tcf2.purpose1.enabled", true) v.SetDefault("gdpr.tcf2.purpose2.enabled", true) diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index 987622a6a8a..a0a73c93008 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -27,8 +27,20 @@ type saveVendors func(uint16, api.VendorList) // Nothing in this file is exported. Public APIs can be found in gdpr.go func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, TCFVer uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { + var fallbackVL api.VendorList = nil + + if TCFVer == tCF1 && len(cfg.TCF1.FallbackGVLPath) > 0 { + fallbackVL = loadFallbackGVL(cfg.TCF1.FallbackGVLPath) + } + + // If we are not going to try fetching the GVL dynamically, we have a simple fetcher + if !cfg.TCF1.FetchGVL && TCFVer == tCF1 && fallbackVL != nil { + return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { + return fallbackVL, nil + } + } // These save and load functions can be used to store & retrieve lists from our cache. - save, load := newVendorListCache() + save, load := newVendorListCache(fallbackVL) withTimeout, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout()) defer cancel() @@ -46,6 +58,9 @@ func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http if list != nil { return list, nil } + if fallbackVL != nil { + return fallbackVL, nil + } return nil, fmt.Errorf("gdpr vendor list version %d does not exist, or has not been loaded yet. Try again in a few minutes", id) } } @@ -132,7 +147,7 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen return newList.Version() } -func newVendorListCache() (save func(id uint16, list api.VendorList), load func(id uint16) api.VendorList) { +func newVendorListCache(fallbackVL api.VendorList) (save func(id uint16, list api.VendorList), load func(id uint16) api.VendorList) { cache := &sync.Map{} save = func(id uint16, list api.VendorList) { @@ -143,7 +158,19 @@ func newVendorListCache() (save func(id uint16, list api.VendorList), load func( if ok { return list.(vendorlist.VendorList) } - return nil + return fallbackVL } return } + +func loadFallbackGVL(fallbackGVLPath string) vendorlist.VendorList { + fallbackVLbody, err := ioutil.ReadFile(fallbackGVLPath) + if err != nil { + glog.Fatalf("Error reading from file %s: %v", fallbackGVLPath, err) + } + fallbackVL, err := vendorlist.ParseEagerly(fallbackVLbody) + if err != nil { + glog.Fatalf("Error processing default GVL from %s: %v", fallbackGVLPath, err) + } + return fallbackVL +} diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 824f9178faa..c989ef4cef8 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/prebid/prebid-server/config" ) @@ -139,6 +141,101 @@ func TestVendorListMaker(t *testing.T) { assertStringsEqual(t, "https://vendorlist.consensu.org/v2/archives/vendor-list-v7.json", vendorListURLMaker(7, 2)) } +func TestDefaultVendorList(t *testing.T) { + firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ + 32: { + purposes: []int{1, 2}, + }, + }) + secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ + 12: { + purposes: []int{2}, + }, + }) + server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ + 1: firstVendorList, + 2: secondVendorList, + }))) + defer server.Close() + + testcfg := testConfig() + testcfg.TCF1.FetchGVL = true + testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" + fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) + + list, err := fetcher(context.Background(), 12) + assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) + assert.Equal(t, uint16(214), list.Version(), "Expected to fetch default version 214, got %d", list.Version()) + + // Testing that we got the default vendorlist data, and not the version off the server. + vendor := list.Vendor(12) + assert.Equal(t, true, vendor.Purpose(1)) + assert.Equal(t, false, vendor.Purpose(2)) +} + +func TestDefaultVendorListPassthrough(t *testing.T) { + firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ + 32: { + purposes: []int{1, 2}, + }, + }) + secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ + 12: { + purposes: []int{2}, + }, + }) + server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ + 1: firstVendorList, + 2: secondVendorList, + }))) + defer server.Close() + + testcfg := testConfig() + testcfg.TCF1.FetchGVL = true + testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" + fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) + list, err := fetcher(context.Background(), 2) + assert.NoError(t, err, "Error with fetching existing vendorlist: %v", err) + assert.Equal(t, uint16(2), list.Version(), "Expected to fetch mock list version 2, got version %d", list.Version()) + + // Testing that we got the testing vendorlist data, and not the default. + vendor := list.Vendor(12) + assert.Equal(t, false, vendor.Purpose(1)) + assert.Equal(t, true, vendor.Purpose(2)) +} + +func TestDefaultVendorListNoFetch(t *testing.T) { + firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ + 32: { + purposes: []int{1, 2}, + }, + }) + secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ + 12: { + purposes: []int{2}, + }, + }) + server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ + 1: firstVendorList, + 2: secondVendorList, + }))) + defer server.Close() + + testcfg := testConfig() + testcfg.TCF1.FetchGVL = false + testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" + fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) + list, err := fetcher(context.Background(), 2) + assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) + assert.Equal(t, uint16(214), list.Version(), "Expected to fetch default version 214, got %d", list.Version()) + + // Testing that we got the default vendorlist data, and not the version off the server. + vendor := list.Vendor(12) + assert.Equal(t, true, vendor.Purpose(1)) + assert.Equal(t, false, vendor.Purpose(2)) + +} + // mockServer returns a handler which returns the given response for each global vendor list version. // The latestVersion param can be used to mock "updates" which occur after PBS has been turned on. // For example, if latestVersion is 3, but the responses map has data at "4", the server will return diff --git a/static/tcf1/fallback_gvl.json b/static/tcf1/fallback_gvl.json new file mode 100644 index 00000000000..86895a52362 --- /dev/null +++ b/static/tcf1/fallback_gvl.json @@ -0,0 +1 @@ +{"vendorListVersion":214,"lastUpdated":"2020-08-06T16:00:35Z","purposes":[{"id":1,"name":"Information storage and access","description":"The storage of information, or access to information that is already stored, on your device such as advertising identifiers, device identifiers, cookies, and similar technologies."},{"id":2,"name":"Personalisation","description":"The collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as on other websites or apps, over time. Typically, the content of the site or app is used to make inferences about your interests, which inform future selection of advertising and/or content."},{"id":3,"name":"Ad selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver advertisements for you, and to measure the delivery and effectiveness of such advertisements. This includes using previously collected information about your interests to select ads, processing data about what advertisements were shown, how often they were shown, when and where they were shown, and whether you took any action related to the advertisement, including for example clicking an ad or making a purchase. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as websites or apps, over time."},{"id":4,"name":"Content selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver content for you, and to measure the delivery and effectiveness of such content. This includes using previously collected information about your interests to select content, processing data about what content was shown, how often or how long it was shown, when and where it was shown, and whether the you took any action related to the content, including for example clicking on content. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, such as websites or apps, over time."},{"id":5,"name":"Measurement","description":"The collection of information about your use of the content, and combination with previously collected information, used to measure, understand, and report on your usage of the service. This does not include personalisation, the collection of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, i.e. on other service, such as websites or apps, over time."}],"features":[{"id":1,"name":"Matching Data to Offline Sources","description":"Combining data from offline sources that were initially collected in other contexts."},{"id":2,"name":"Linking Devices","description":"Allow processing of a user's data to connect such user across multiple devices."},{"id":3,"name":"Precise Geographic Location Data","description":"Allow processing of a user's precise geographic location data in support of a purpose for which that certain third party has consent."}],"vendors":[{"id":8,"name":"Emerse Sverige AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.emerse.com/privacy-policy/"},{"id":9,"name":"AdMaxim Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.admaxim.com/admaxim-privacy-policy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":12,"name":"BeeswaxIO Corporation","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beeswax.com/privacy/"},{"id":28,"name":"TripleLift, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://triplelift.com/privacy/"},{"id":27,"name":"ADventori SAS","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adventori.com/with-us/legal-notice/"},{"id":25,"name":"Verizon Media EMEA Limited","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.verizonmedia.com/policies/ie/en/verizonmedia/privacy/index.html"},{"id":26,"name":"Venatus Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.venatusmedia.com/privacy/"},{"id":1,"name":"Exponential Interactive, Inc d/b/a VDX.tv","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://vdx.tv/privacy/"},{"id":6,"name":"AdSpirit GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adspirit.de/privacy"},{"id":30,"name":"BidTheatre AB","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.bidtheatre.com/privacy-policy"},{"id":24,"name":"Epsilon","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.conversantmedia.eu/legal/privacy-policy"},{"id":29,"name":"Etarget SE","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.etarget.sk/privacy.php","deletedDate":"2020-06-01T00:00:00Z"},{"id":39,"name":"ADITION technologies AG","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.adition.com/datenschutz"},{"id":11,"name":"Quantcast International Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.quantcast.com/privacy/"},{"id":15,"name":"Adikteev","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adikteev.com/privacy-policy-eng/"},{"id":4,"name":"Roq.ad Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.roq.ad/privacy-policy"},{"id":7,"name":"Vibrant Media Limited","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vibrantmedia.com/en/privacy-policy/"},{"id":2,"name":"Captify Technologies Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.captify.co.uk/privacy-policy/"},{"id":37,"name":"NEURAL.ONE","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://web.neural.one/privacy-policy/"},{"id":13,"name":"Sovrn Holdings Inc","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sovrn.com/sovrn-privacy/"},{"id":34,"name":"NEORY GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.neory.com/privacy.html"},{"id":32,"name":"Xandr, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.xandr.com/privacy/platform-privacy-policy/"},{"id":10,"name":"Index Exchange, Inc. ","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.indexexchange.com/privacy"},{"id":57,"name":"ADARA MEDIA UNLIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://adara.com/privacy-promise/"},{"id":63,"name":"Avocet Systems Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://avocet.io/privacy-portal"},{"id":51,"name":"xAd, Inc. dba GroundTruth","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.groundtruth.com/privacy-policy/"},{"id":49,"name":"TRADELAB","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://tradelab.com/en/privacy/"},{"id":45,"name":"Smart Adserver","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://smartadserver.com/end-user-privacy-policy/"},{"id":52,"name":"The Rubicon Project, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[3],"policyUrl":"http://www.rubiconproject.com/rubicon-project-yield-optimization-privacy-policy/"},{"id":71,"name":"Roku Advertising Services","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://docs.roku.com/published/userprivacypolicy/en/us"},{"id":79,"name":"MediaMath, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.mediamath.com/privacy-policy/"},{"id":91,"name":"Criteo SA","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.criteo.com/privacy/"},{"id":85,"name":"Crimtan Holdings Limited","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[1,3],"policyUrl":"https://crimtan.com/privacy/"},{"id":16,"name":"RTB House S.A.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.rtbhouse.com/privacy-center/services-privacy-policy/"},{"id":86,"name":"Scene Stealer Limited","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"http://scenestealer.tv/privacy-policy/"},{"id":94,"name":"Blis Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.blis.com/privacy/"},{"id":73,"name":"Simplifi Holdings Inc.","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2,3],"policyUrl":"https://simpli.fi/site-privacy-policy/"},{"id":33,"name":"ShareThis, Inc","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://sharethis.com/privacy/"},{"id":20,"name":"N Technologies Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://n.rich/privacy-notice"},{"id":55,"name":"Madison Logic, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.madisonlogic.com/privacy/"},{"id":53,"name":"Sirdata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.sirdata.com/privacy/"},{"id":69,"name":"OpenX","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.openx.com/legal/privacy-policy/"},{"id":98,"name":"GroupM UK Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.groupm.com/privacy-notice"},{"id":62,"name":"Justpremium BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://justpremium.com/privacy-policy/"},{"id":19,"name":"Intent Media, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://intentmedia.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":43,"name":"Vdopia DBA Chocolate Platform","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://chocolateplatform.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":36,"name":"RhythmOne DBA Unruly Group Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.rhythmone.com/privacy-policy"},{"id":80,"name":"Sharethrough, Inc","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://platform-cdn.sharethrough.com/privacy-policy"},{"id":81,"name":"PulsePoint, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pulsepoint.com/privacy-policy/website","deletedDate":"2020-07-06T00:00:00Z"},{"id":23,"name":"Amobee, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.amobee.com/trust/privacy-guidelines"},{"id":35,"name":"Purch Group, Inc.","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://www.purch.com/privacy-policy/","deletedDate":"2019-05-30T00:00:00Z"},{"id":3,"name":"affilinet","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.affili.net/de/footeritem/datenschutz","deletedDate":"2019-06-21T00:00:00Z"},{"id":74,"name":"Admotion SRL","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.admotion.com/policy/","deletedDate":"2019-07-24T00:00:00Z"},{"id":191,"name":"realzeit GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://realzeitmedia.com/privacy.html","deletedDate":"2019-04-29T00:00:00Z"},{"id":197,"name":"Switch Concepts Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.switchconcepts.com/privacy-policy","deletedDate":"2019-07-26T00:00:00Z"},{"id":390,"name":"Parsec Media Inc.","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,3],"policyUrl":"www.parsec.media/privacy-policy","deletedDate":"2019-06-27T00:00:00Z"},{"id":459,"name":"uppr GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://netzwerk.uppr.de/privacy-policy.do","deletedDate":"2019-06-17T00:00:00Z"},{"id":221,"name":"LEMO MEDIA GROUP LIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.lemomedia.com/terms.pdf","deletedDate":"2019-06-28T00:00:00Z"},{"id":478,"name":"RevLifter Ltd","purposeIds":[1],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.revlifter.com/privacy-policy","deletedDate":"2019-07-15T00:00:00Z"},{"id":500,"name":"Turbo","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.turboadv.com/white-rabbit-privacy-policy/","deletedDate":"2019-07-12T00:00:00Z"},{"id":68,"name":"Sizmek by Amazon","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.sizmek.com/privacy-policy/"},{"id":75,"name":"M32 Connect Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://m32.media/privacy-cookie-policy/"},{"id":17,"name":"Greenhouse Group BV (with its trademark LemonPI)","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.lemonpi.io/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":61,"name":"GumGum, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://gumgum.com/privacy-policy"},{"id":40,"name":"Active Agent (ADITION technologies AG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.active-agent.com/de/unternehmen/datenschutzerklaerung/"},{"id":76,"name":"PubMatic, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://pubmatic.com/privacy-policy/"},{"id":89,"name":"Tapad, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.tapad.com/eu-privacy-policy"},{"id":46,"name":"Skimbit Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://skimlinks.com/pages/privacy-policy"},{"id":66,"name":"adsquare GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adsquare.com/privacy"},{"id":105,"name":"Impression Desk Technologies Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://impressiondesk.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":41,"name":"Adverline","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.adverline.com/privacy/"},{"id":82,"name":"Smaato, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.smaato.com/privacy/"},{"id":60,"name":"Rakuten Marketing LLC","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://rakutenadvertising.com/legal-notices/services-privacy-policy/"},{"id":70,"name":"Yieldlab AG","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[3],"policyUrl":"http://www.yieldlab.de/meta-navigation/datenschutz/"},{"id":50,"name":"Adform","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://site.adform.com/privacy-center/platform-privacy/product-and-services-privacy-policy/"},{"id":48,"name":"NetSuccess, s.r.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inres.sk/pp/"},{"id":100,"name":"Fifty Technology Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://fifty.io/privacy-policy.php"},{"id":21,"name":"The Trade Desk","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.thetradedesk.com/general/privacy-policy"},{"id":110,"name":"Dynata LLC","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.opinionoutpost.co.uk/en-gb/policies/privacy"},{"id":42,"name":"Taboola Europe Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.taboola.com/privacy-policy"},{"id":112,"name":"Maytrics GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://maytrics.com/privacy.php","deletedDate":"2019-09-17T00:00:00Z"},{"id":77,"name":"comScore, Inc.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.scorecardresearch.com/privacy.aspx?newlanguage=1"},{"id":109,"name":"LoopMe Limited","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://loopme.com/privacy-policy/"},{"id":120,"name":"Eyeota Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.eyeota.com/privacy-center"},{"id":93,"name":"Adloox SA","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://adloox.com/disclaimer"},{"id":132,"name":"Teads ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.teads.com/privacy-policy/"},{"id":22,"name":"admetrics GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://admetrics.io/en/privacy_policy/"},{"id":102,"name":"Telaria SAS","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":108,"name":"Rich Audience Technologies SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://richaudience.com/privacy/"},{"id":18,"name":"Widespace AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.widespace.com/legal/privacy-policy-notice/"},{"id":122,"name":"Avid Media Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.avidglobalmedia.eu/privacy-policy.html"},{"id":97,"name":"LiveRamp, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.liveramp.com/service-privacy-policy/"},{"id":138,"name":"ConnectAd Realtime GmbH","purposeIds":[1,2],"legIntPurposeIds":[3,4],"featureIds":[],"policyUrl":"http://connectadrealtime.com/privacy/"},{"id":72,"name":"Nano Interactive GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.nanointeractive.com/privacy"},{"id":127,"name":"PIXIMEDIA SAS","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://piximedia.com/privacy/"},{"id":136,"name":"Str\u00f6er SSP GmbH (SSP)","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[2,3],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":111,"name":"Showheroes SE","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://showheroes.com/privacy/"},{"id":56,"name":"Confiant Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.confiant.com/privacy","deletedDate":"2020-05-18T00:00:00Z"},{"id":124,"name":"Teemo SA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://teemo.co/fr/confidentialite/"},{"id":154,"name":"YOC AG","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://yoc.com/privacy/"},{"id":38,"name":"Beemray Oy","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beemray.com/privacy-policy/","deletedDate":"2020-06-19T00:00:00Z"},{"id":101,"name":"MiQ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://wearemiq.com/privacy-policy/"},{"id":149,"name":"ADman Interactive SLU","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://admanmedia.com/politica.html?setLng=es"},{"id":151,"name":"Admedo Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[3],"policyUrl":"https://www.admedo.com/privacy-policy","deletedDate":"2020-07-17T00:00:00Z"},{"id":153,"name":"MADVERTISE MEDIA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://madvertise.com/en/gdpr/"},{"id":159,"name":"Underdog Media LLC ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://underdogmedia.com/privacy-policy/"},{"id":157,"name":"Seedtag Advertising S.L","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.seedtag.com/en/privacy-policy/"},{"id":145,"name":"Snapsort Inc., operating as Sortable","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://help.sortable.com/help/privacy-policy"},{"id":131,"name":"ID5 Technology SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.id5.io/privacy"},{"id":158,"name":"Reveal Mobile, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://revealmobile.com/privacy"},{"id":147,"name":"Adacado Technologies Inc. (DBA Adacado)","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adacado.com/privacy-policy-april-25-2018/"},{"id":130,"name":"NextRoll, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.nextroll.com/privacy"},{"id":129,"name":"IPONWEB GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.iponweb.com/privacy-policy/"},{"id":128,"name":"BIDSWITCH GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bidswitch.com/privacy-policy/"},{"id":168,"name":"EASYmedia GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://login.rtbmarket.com/gdpr"},{"id":164,"name":"Outbrain UK Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.outbrain.com/legal/privacy#privacy-policy"},{"id":144,"name":"district m inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://districtm.net/en/page/platforms-data-and-privacy-policy/"},{"id":163,"name":"Bombora Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://bombora.com/privacy"},{"id":173,"name":"Yieldmo, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.yieldmo.com/privacy/"},{"id":88,"name":"TreSensa, Inc.","purposeIds":[1,3],"legIntPurposeIds":[2,5],"featureIds":[1],"policyUrl":"https://www.tresensa.com/eu-privacy"},{"id":78,"name":"Flashtalking, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.flashtalking.com/privacypolicy/"},{"id":59,"name":"Sift Media, Inc","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.sift.co/privacy"},{"id":114,"name":"Sublime","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://ayads.co/privacy.php"},{"id":175,"name":"FORTVISION","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://fortvision.com/POC/index.html","deletedDate":"2019-08-09T00:00:00Z"},{"id":133,"name":"digitalAudience","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://digitalaudience.io/legal/privacy-cookies/"},{"id":14,"name":"Adkernel LLC","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://adkernel.com/privacy-policy/"},{"id":180,"name":"Thirdpresence Oy","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"http://www.thirdpresence.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":183,"name":"EMX Digital LLC","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://emxdigital.com/privacy/"},{"id":58,"name":"33Across","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.33across.com/privacy-policy"},{"id":140,"name":"Platform161","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://platform161.com/cookie-and-privacy-policy/"},{"id":90,"name":"Teroa S.A.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.e-planning.net/en/privacy.html"},{"id":141,"name":"1020, Inc. dba Placecast and Ericsson Emodo","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.emodoinc.com/privacy-policy/"},{"id":142,"name":"Media.net Advertising FZ-LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.media.net/en/privacy-policy"},{"id":209,"name":"Delta Projects AB","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[3],"policyUrl":"https://deltaprojects.com/data-collection-policy"},{"id":195,"name":"advanced store GmbH","purposeIds":[2,3],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.advanced-store.com/de/datenschutz/"},{"id":190,"name":"video intelligence AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.vi.ai/privacy-policy/"},{"id":84,"name":"Semasio GmbH","purposeIds":[],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"http://www.semasio.com/privacy-policy/"},{"id":65,"name":"Location Sciences AI Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.locationsciences.ai/privacy-policy/"},{"id":210,"name":"Zemanta, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1],"policyUrl":"http://www.zemanta.com/legal/privacy"},{"id":200,"name":"Tapjoy, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.tapjoy.com/legal/#privacy-policy"},{"id":188,"name":"Sellpoints Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://retargeter.com/service-privacy-policy/","deletedDate":"2019-09-17T00:00:00Z"},{"id":217,"name":"2KDirect, Inc. (dba iPromote)","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.ipromote.com/privacy-policy/"},{"id":156,"name":"Centro, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.centro.net/privacy-policy/"},{"id":194,"name":"Rezonence Limited","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://rezonence.com/privacy-policy/"},{"id":226,"name":"Publicis Media GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.publicismedia.de/datenschutz/"},{"id":198,"name":"SYNC","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://redirect.sync.tv/privacy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":227,"name":"ORTEC B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.ortecadscience.com/privacy-policy/"},{"id":225,"name":"Ligatus GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.ligatus.com/en/privacy-policy","deletedDate":"2020-06-19T00:00:00Z"},{"id":205,"name":"Adssets AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://adssets.com/policy/"},{"id":179,"name":"Collective Europe Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.collectiveuk.com/privacy.html"},{"id":31,"name":"Ogury Ltd.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://www.ogury.com/privacy-policy/"},{"id":92,"name":"1plusX AG","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.1plusx.com/privacy-policy/"},{"id":155,"name":"AntVoice","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.antvoice.com/en/privacypolicy/"},{"id":115,"name":"smartclip Europe GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://privacy-portal.smartclip.net/"},{"id":126,"name":"DoubleVerify Inc.\u200b","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.doubleverify.com/privacy/"},{"id":193,"name":"Mediasmart Mobile S.L.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://mediasmart.io/privacy/"},{"id":245,"name":"IgnitionOne","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.ignitionone.com/privacy-policy/","deletedDate":"2020-06-30T00:00:00Z"},{"id":213,"name":"emetriq GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.emetriq.com/datenschutz/"},{"id":244,"name":"Temelio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://temelio.com/vie-privee"},{"id":224,"name":"adrule mobile GmbH","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.adrule.net/de/datenschutz/"},{"id":174,"name":"A Million Ads Ltd","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.amillionads.com/privacy-policy"},{"id":192,"name":"remerge GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://remerge.io/privacy-policy.html"},{"id":232,"name":"Rockerbox, Inc","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"http://rockerbox.com/privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":256,"name":"Bounce Exchange, Inc","purposeIds":[1],"legIntPurposeIds":[2,4,5],"featureIds":[1,2],"policyUrl":"https://www.bouncex.com/privacy/"},{"id":234,"name":"ZBO Media","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zbo.media/mentions-legales/politique-de-confidentialite-service-publicitaire/"},{"id":246,"name":"Smartology Limited","purposeIds":[3],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://www.smartology.net/privacy-policy/"},{"id":241,"name":"OneTag Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.onetag.com/privacy/"},{"id":254,"name":"LiquidM Technology GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liquidm.com/privacy-policy/"},{"id":215,"name":"ARMIS SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://armis.tech/en/armis-personal-data-privacy-policy/"},{"id":167,"name":"Audiens S.r.l.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.audiens.com/privacy"},{"id":240,"name":"7Hops.com Inc. (ZergNet)","purposeIds":[],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://zergnet.com/privacy"},{"id":235,"name":"Bucksense Inc","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.bucksense.com/platform-privacy-policy/"},{"id":185,"name":"Bidtellect, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.bidtellect.com/privacy-policy/"},{"id":258,"name":"Adello Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.adello.com/privacy-policy/"},{"id":169,"name":"RTK.IO, Inc","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://www.rtk.io/privacy.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":208,"name":"Spotad","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.spotad.co/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":211,"name":"AdTheorent, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://adtheorent.com/privacy-policy"},{"id":229,"name":"Digitize New Media Ltd","purposeIds":[2,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitize.ie/online-privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":273,"name":"Bannerflow AB","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.bannerflow.com/privacy "},{"id":104,"name":"Sonobi, Inc","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"http://sonobi.com/privacy-policy/"},{"id":162,"name":"Unruly Group Ltd","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://unruly.co/privacy/"},{"id":249,"name":"Spolecznosci Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.spolecznosci.pl/polityka-prywatnosci"},{"id":125,"name":"Research Now Group, Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.valuedopinions.co.uk/privacy","deletedDate":"2019-09-17T00:00:00Z"},{"id":170,"name":"Goodway Group, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://goodwaygroup.com/privacy-policy/"},{"id":160,"name":"Netsprint SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://netsprint.eu/privacy.html"},{"id":189,"name":"Intowow Innovation Ltd.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.intowow.com/privacy/","deletedDate":"2019-08-12T00:00:00Z"},{"id":279,"name":"Mirando GmbH & Co KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://wwwmirando.de/datenschutz/"},{"id":269,"name":"Sanoma Media Finland","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://sanoma.fi/tietoa-meista/tietosuoja/","deletedDate":"2019-08-07T00:00:00Z"},{"id":276,"name":"Viralize SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://viralize.com/privacy-policy"},{"id":87,"name":"Genius Sports Media Limited","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[2,3],"policyUrl":"https://www.geniussports.com/privacy-policy"},{"id":182,"name":"Collective, Inc. dba Visto","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vistohub.com/privacy-policy/","deletedDate":"2019-07-26T00:00:00Z"},{"id":255,"name":"Onnetwork Sp. z o.o.","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.onnetwork.tv/pp_services.php"},{"id":203,"name":"Revcontent, LLC","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://intercom.help/revcontent2/en/articles/2290675-revcontent-s-privacy-policy"},{"id":260,"name":"RockYou, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,5],"featureIds":[3],"policyUrl":"https://rockyou.com/privacy-policy/","deletedDate":"2019-08-09T00:00:00Z"},{"id":237,"name":"LKQD, a division of Nexstar Digital, LLC.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.lkqd.com/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":274,"name":"Golden Bees","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.goldenbees.fr/en/privacy-charter/"},{"id":280,"name":"Spot.IM LTD","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.spot.im/privacy/"},{"id":239,"name":"Triton Digital Canada Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.tritondigital.com/privacy-policies"},{"id":177,"name":"plista GmbH","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.plista.com/about/privacy/"},{"id":201,"name":"TimeOne","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://privacy.timeonegroup.com/en/","deletedDate":"2020-05-15T00:00:00Z"},{"id":150,"name":"Inskin Media LTD","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.inskinmedia.com/privacy-policy.html"},{"id":252,"name":"Jaduda GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.jadudamobile.com/datenschutzerklaerung/"},{"id":248,"name":"Converge-Digital","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://converge-digital.com/privacy-policy/"},{"id":161,"name":"Smadex SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://smadex.com/end-user-privacy-policy/"},{"id":285,"name":"Comcast International France SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.freewheel.com/privacy-policy"},{"id":228,"name":"McCann Discipline LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.primis.tech/privacy-policy/"},{"id":299,"name":"AdClear GmbH","purposeIds":[1,5],"legIntPurposeIds":[2,3,4],"featureIds":[1,2],"policyUrl":"https://www.adclear.de/datenschutzerklaerung/"},{"id":277,"name":"Codewise VL Sp. z o.o. Sp. k","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://voluumdsp.com/end-user-privacy-policy/"},{"id":259,"name":"ADYOULIKE SA","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.adyoulike.com/privacy_policy.php"},{"id":272,"name":"A.Mob","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.we-are-adot.com/privacy-policy/"},{"id":230,"name":"Steel House, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://steelhouse.com/privacy-policy/"},{"id":253,"name":"Improve Digital BV","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.improvedigital.com/platform-privacy-policy"},{"id":304,"name":"On Device Research Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://s.on-device.com/privacyPolicy"},{"id":314,"name":"Keymantics","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.keymantics.com/assets/privacy-policy.pdf"},{"id":257,"name":"R-TARGET","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"http://www.r-target.com/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":317,"name":"mainADV Srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.mainad.com/privacy-policy/"},{"id":278,"name":"Integral Ad Science, Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://integralads.com/privacy-policy/"},{"id":291,"name":"Qwertize","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.qwertize.com/en/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":295,"name":"Sojern, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.sojern.com/privacy/product-privacy-policy/"},{"id":315,"name":"Celtra, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.celtra.com/privacy-policy/"},{"id":165,"name":"SpotX, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.spotx.tv/privacy-policy/"},{"id":47,"name":"ADMAN - Phaistos Networks, S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adman.gr/privacy"},{"id":134,"name":"SMARTSTREAM.TV GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://www.smartstream.tv/en/productprivacy"},{"id":325,"name":"Knorex","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.knorex.com/privacy"},{"id":316,"name":"Gamned","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.gamned.com/privacy-policy/"},{"id":318,"name":"Accorp Sp. z o.o.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"http://www.instytut-pollster.pl/privacy-policy/"},{"id":199,"name":"ADUX","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adux.com/donnees-personelles/"},{"id":236,"name":"PowerLinks Media Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[3],"policyUrl":"https://www.powerlinks.com/privacy-policy/"},{"id":294,"name":"Jivox Corporation","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.jivox.com/privacy"},{"id":143,"name":"Connatix Native Exchange Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://connatix.com/privacy-policy/"},{"id":297,"name":"Polar Mobile Group Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://privacy.polar.me"},{"id":319,"name":"Clipcentric, Inc.","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://clipcentric.com/privacy.bhtml"},{"id":290,"name":"Readpeak Oy","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://readpeak.com/privacy-policy/"},{"id":323,"name":"DAZN Media Services Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.goal.com/en-gb/legal/privacy-policy"},{"id":119,"name":"Fusio by S4M","purposeIds":[1,2,5],"legIntPurposeIds":[3],"featureIds":[1,3],"policyUrl":"http://www.s4m.io/privacy-policy/"},{"id":302,"name":"Mobile Professionals BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mobpro.com/privacy.html"},{"id":212,"name":"usemax advertisement (Emego GmbH)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.usemax.de/?l=privacy"},{"id":264,"name":"Adobe Advertising Cloud","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.adobe.com/privacy/experience-cloud.html"},{"id":44,"name":"The ADEX GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://theadex.com/privacy-opt-out/"},{"id":282,"name":"Welect GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.welect.de/datenschutz"},{"id":238,"name":"StackAdapt","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.stackadapt.com/privacy"},{"id":284,"name":"WEBORAMA","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://weborama.com/privacy_en/"},{"id":148,"name":"Liveintent Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://liveintent.com/services-privacy-policy/"},{"id":64,"name":"DigiTrust / IAB Tech Lab","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitru.st/privacy-policy/"},{"id":301,"name":"zeotap GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://zeotap.com/privacy_policy"},{"id":275,"name":"TabMo SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://static.tabmo.io.s3.amazonaws.com/privacy-policy/index.html"},{"id":310,"name":"Adevinta Spain S.L.U.","purposeIds":[],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"https://www.adevinta.com/about/privacy/"},{"id":139,"name":"Permodo GmbH","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://permodo.com/de/privacy.html"},{"id":326,"name":"AdTiming Technology Company Limited","purposeIds":[3,5],"legIntPurposeIds":[1,2,4],"featureIds":[],"policyUrl":"http://www.adtiming.com/en/privacypolicy.html"},{"id":262,"name":"Fyber ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.fyber.com/legal/privacy-policy/"},{"id":331,"name":"ad6media","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.ad6media.fr/privacy"},{"id":345,"name":"The Kantar Group Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.kantar.com/cookies-policies"},{"id":308,"name":"Rockabox Media Ltd","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[],"policyUrl":"http://scoota.com/privacy-policy"},{"id":270,"name":"Marfeel Solutions, SL","purposeIds":[],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.marfeel.com/privacy-policy/"},{"id":333,"name":"InMobi Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":202,"name":"Telaria, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":328,"name":"Gemius SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.gemius.com/cookie-policy.html"},{"id":281,"name":"Wizaly","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.wizaly.com/terms-of-use#privacy-policy"},{"id":354,"name":"Apester Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://apester.com/privacy-policy/"},{"id":320,"name":"Adelphic LLC","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://adelphic.com/platform/privacy/"},{"id":359,"name":"AerServ LLC","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":265,"name":"Instinctive, Inc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://instinctive.io/privacy"},{"id":349,"name":"Optomaton UG","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://optomaton.com/privacy.html"},{"id":288,"name":"Video Media Groep B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://www.videomediagroup.com/wp-content/uploads/2016/01/Privacy-policy-VMG.pdf","deletedDate":"2019-09-17T00:00:00Z"},{"id":266,"name":"Digilant Spain, SLU","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.digilant.com/es/politica-privacidad/"},{"id":339,"name":"Vuble","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vuble.tv/us/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":303,"name":"Orion Semantics","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://static.orion-semantics.com/privacy.html"},{"id":261,"name":"Signal Digital Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.signal.co/privacy-policy/"},{"id":83,"name":"Visarity Technologies GmbH","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://primo.design/docs/PrivacyPolicyPrimo.html"},{"id":343,"name":"DIGITEKA Technologies","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.ultimedia.com/POLICY.html"},{"id":330,"name":"Linicom","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.linicom.com/privacy/","deletedDate":"2020-06-08T00:00:00Z"},{"id":231,"name":"AcuityAds Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.acuityads.com/corporate-privacy-policy.html"},{"id":216,"name":"Mindlytix SAS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://mindlytix.com/privacy/"},{"id":360,"name":"Permutive Technologies, Inc.","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[1,2],"policyUrl":"https://permutive.com/privacy","deletedDate":"2020-03-31T00:00:00Z"},{"id":361,"name":"Permutive","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://permutive.com/privacy"},{"id":311,"name":"Mobfox US LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobfox.com/privacy-policy/"},{"id":358,"name":"MGID Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mgid.com/privacy-policy"},{"id":152,"name":"Meetrics GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.meetrics.com/en/data-privacy/"},{"id":251,"name":"Yieldlove GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"http://www.yieldlove.com/cookie-policy"},{"id":344,"name":"My6sense Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[2,4],"featureIds":[],"policyUrl":"https://my6sense.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":347,"name":"Ezoic Inc.","purposeIds":[2,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.ezoic.com/terms/"},{"id":218,"name":"Bigabid Media ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.bigabid.com/privacy-policy"},{"id":350,"name":"Free Stream Media Corp. dba Samba TV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":351,"name":"Samba TV UK Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":341,"name":"Somo Audience Corp","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"https://somoaudience.com/legal/","deletedDate":"2020-07-06T00:00:00Z"},{"id":380,"name":"Vidoomy Media SL","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"http://vidoomy.com/privacy-policy.html"},{"id":378,"name":"communicationAds GmbH & Co. KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.communicationads.net/aboutus/privacy/"},{"id":369,"name":"Getintent USA, inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://getintent.com/privacy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":184,"name":"mediarithmics SAS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mediarithmics.com/en-us/content/privacy-policy"},{"id":368,"name":"VECTAURY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vectaury.io/en/personal-data"},{"id":373,"name":"Nielsen Marketing Cloud","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"http://www.nielsen.com/us/en/privacy-statement/exelate-privacy-policy.html"},{"id":214,"name":"Digital Control GmbH & Co. KG","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://advolution.de/privacy.php","deletedDate":"2020-05-06T00:00:00Z"},{"id":388,"name":"numberly","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://numberly.com/en/privacy/"},{"id":250,"name":"Qriously Ltd","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.brandwatch.com/legal/qriously-privacy-notice/"},{"id":223,"name":"Audience Trading Platform Ltd.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://atp.io/privacy-policy"},{"id":384,"name":"Pixalate, Inc.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"http://pixalate.com/privacypolicy/","deletedDate":"2019-11-08T00:00:00Z"},{"id":387,"name":"Triapodi Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appreciate.mobi/page.html#/end-user-privacy-policy"},{"id":312,"name":"Exactag GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.exactag.com/en/data-privacy/"},{"id":178,"name":"Hybrid Theory","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://hybridtheory.com/privacy-policy/"},{"id":377,"name":"AddApptr GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.addapptr.com/data-privacy"},{"id":382,"name":"The Reach Group GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://trg.de/en/privacy-statement/"},{"id":206,"name":"Hybrid Adtech GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://hybrid.ai/data_protection_policy"},{"id":403,"name":"Mobusi Mobile Advertising S.L.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobusi.com/privacy.en.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":385,"name":"Oracle Data Cloud","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html"},{"id":404,"name":"Duplo Media AS","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.easy-ads.com/privacypolicy.htm"},{"id":242,"name":"twiago GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.twiago.com/datenschutz/"},{"id":376,"name":"Pocketmath Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pocketmath.com/privacy-policy"},{"id":402,"name":"Effiliation","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://inter.effiliation.com/politique-confidentialite.html"},{"id":413,"name":"Eulerian Technologies","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.eulerian.com/en/privacy/"},{"id":400,"name":"Whenever Media Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.whenevermedia.com/privacy-policy","deletedDate":"2019-07-29T00:00:00Z"},{"id":171,"name":"Webedia","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webedia-group.com/site/privacy-policy","deletedDate":"2020-07-01T00:00:00Z"},{"id":398,"name":"Yormedia Solutions Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.yormedia.com/privacy-and-cookies-notice/","deletedDate":"2019-08-06T00:00:00Z"},{"id":415,"name":"Seenthis AB","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://seenthis.co/privacy-notice-2018-04-18.pdf"},{"id":263,"name":"Nativo, Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.nativo.com/interest-based-ads"},{"id":329,"name":"Browsi Mobile Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://gobrowsi.com/browsi-privacy-policy/"},{"id":389,"name":"Bidmanagement GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adspert.net/en/privacy/","deletedDate":"2020-07-01T00:00:00Z"},{"id":337,"name":"SheMedia, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shemedia.com/ad-services-privacy-policy"},{"id":422,"name":"Brand Metrics Sweden AB","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://collector.brandmetrics.com/brandmetrics_privacypolicy.pdf"},{"id":421,"name":"LeftsnRight, Inc. dba LIQWID","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liqwid.solutions/privacy-policy","deletedDate":"2020-06-30T00:00:00Z"},{"id":426,"name":"TradeTracker","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[2],"policyUrl":"https://tradetracker.com/privacy-policy/","deletedDate":"2019-08-21T00:00:00Z"},{"id":394,"name":"AudienceProject Aps","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://privacy.audienceproject.com"},{"id":287,"name":"Avazu Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4],"featureIds":[3],"policyUrl":"http://avazuinc.com/opt-out/","deletedDate":"2020-08-03T00:00:00Z"},{"id":243,"name":"Cloud Technologies S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cloudtechnologies.pl/en/internet-advertising-privacy-policy"},{"id":113,"name":"iotec global Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.iotecglobal.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":338,"name":"dunnhumby Germany GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.sociomantic.com/privacy/en/","deletedDate":"2020-07-17T00:00:00Z"},{"id":405,"name":"IgnitionAi Ltd","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[2],"policyUrl":"https://www.isitelab.io/default.aspx","deletedDate":"2020-07-03T00:00:00Z"},{"id":416,"name":"Commanders Act","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.commandersact.com/en/privacy/"},{"id":434,"name":"DynAdmic","purposeIds":[1,3],"legIntPurposeIds":[2,4],"featureIds":[1,3],"policyUrl":"http://eu.dynadmic.com/privacy-policy/"},{"id":435,"name":"SINGLESPOT SAS ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.singlespot.com/privacy_policy?locale=fr"},{"id":409,"name":"Arrivalist Co.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[1,2],"policyUrl":"https://www.arrivalist.com/privacy"},{"id":321,"name":"Ziff Davis LLC","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.ziffdavis.com/privacy-policy"},{"id":436,"name":"INVIBES GROUP","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[1,2,3],"policyUrl":"http://www.invibes.com/terms"},{"id":442,"name":"R-Advertising","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-20T00:00:00Z"},{"id":362,"name":"Myntelligence S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://myntelligence.com/privacy-page/"},{"id":418,"name":"PROXISTORE","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://www.proxistore.com/common/en/cgv"},{"id":449,"name":"Mobile Journey B.V.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://mobilejourney.com/Privacy-Policy","deletedDate":"2019-09-05T00:00:00Z"},{"id":443,"name":"Tradedoubler AB","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-13T00:00:00Z"},{"id":429,"name":"Signals","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://signalsdata.com/platform-cookie-policy/"},{"id":335,"name":"Beachfront Media LLC","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://beachfront.com/privacy-policy/"},{"id":407,"name":"Publishers Internationale Pty Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pi-rate.com.au/privacy.html","deletedDate":"2019-11-08T00:00:00Z"},{"id":427,"name":"Proxi.cloud Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://proxi.cloud/info/privacy-policy/"},{"id":374,"name":"Bmind a Sales Maker Company, S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bmind.es/legal-notice/"},{"id":438,"name":"INVIDI technologies AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.invidi.com/wp-content/uploads/2020/02/ad-tech-services-privacy-policy.pdf"},{"id":450,"name":"Neodata Group srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.neodatagroup.com/en/security-policy"},{"id":452,"name":"Innovid Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.innovid.com/privacy-policy"},{"id":444,"name":"Playbuzz Ltd (aka EX.CO)","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://ex.co/privacy-policy/"},{"id":412,"name":"Cxense ASA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.cxense.com/about-us/privacy-policy"},{"id":454,"name":"Adimo","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://adimo.co/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":455,"name":"GDMServices, Inc. d/b/a FiksuDSP","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://fiksu.com/privacy-policy/"},{"id":298,"name":"Cuebiq Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.cuebiq.com/privacypolicy/","deletedDate":"2019-08-30T00:00:00Z"},{"id":423,"name":"travel audience GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://travelaudience.com/product-privacy-policy/"},{"id":397,"name":"Demandbase, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.demandbase.com/privacy-policy/"},{"id":381,"name":"Solocal","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://frontend.adhslx.com/privacy.html?"},{"id":425,"name":"ADRINO Sp. z o.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.adrino.pl/ciasteczkowa-polityka/","deletedDate":"2019-09-05T00:00:00Z"},{"id":365,"name":"Forensiq LLC","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1,3],"policyUrl":"https://impact.com/privacy-policy/"},{"id":447,"name":"Adludio Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adludio.com/privacy-policy/"},{"id":410,"name":"Adtelligent Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtelligent.com/privacy-policy/"},{"id":137,"name":"Str\u00f6er SSP GmbH (DSP)","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":395,"name":"PREX Programmatic Exchange GmbH&Co KG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[],"policyUrl":"http://www.programmatic-exchange.com/privacy","deletedDate":"2020-07-03T00:00:00Z"},{"id":462,"name":"Bidstack Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[2],"policyUrl":"https://www.bidstack.com/privacy-policy/"},{"id":466,"name":"TACTIC\u2122 Real-Time Marketing AS","purposeIds":[],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://tacticrealtime.com/privacy/"},{"id":340,"name":"Yieldr UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.yieldr.com/privacy"},{"id":336,"name":"Telecoming S.A.","purposeIds":[3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.telecoming.com/privacy-policy/"},{"id":430,"name":"Ad Unity Ltd","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"http://www.adunity.com/privacy-policy.html","deletedDate":"2019-08-13T00:00:00Z"},{"id":346,"name":"Cybba, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://cybba.com/about/legal/data-processing-agreement/","deletedDate":"2020-08-03T00:00:00Z"},{"id":469,"name":"Zeta Global","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://zetaglobal.com/privacy-policy/"},{"id":440,"name":"DEFINE MEDIA GMBH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.definemedia.de/datenschutz-conative/"},{"id":375,"name":"Affle International","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://affle.com/privacy-policy "},{"id":196,"name":"AdElement Media Solutions Pvt Ltd","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"http://adelement.com/privacy-policy.html"},{"id":268,"name":"Social Tokens Ltd. ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://woobi.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":475,"name":"TAPTAP Digital SL","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1,2,3],"policyUrl":"http://www.taptapnetworks.com/privacy_policy/"},{"id":474,"name":"hbfsTech","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.hbfstech.com/fr/privacy.html"},{"id":448,"name":"Targetspot Belgium SPRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://marketing.targetspot.com/Targetspot/Legal/TargetSpot%20Privacy%20Policy%20-%20June%202018.pdf"},{"id":428,"name":"Internet BillBoard a.s.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.ibillboard.com/en/privacy-information/"},{"id":461,"name":"B2B Media Group EMEA GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selfcampaign.com/static/privacy","deletedDate":"2019-08-14T00:00:00Z"},{"id":476,"name":"HIRO Media Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"http://hiro-media.com/privacy.php"},{"id":480,"name":"pilotx.tv","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[1,2,3],"policyUrl":"https://pilotx.tv/privacy/"},{"id":366,"name":"CerebroAd.com s.r.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.cerebroad.com/privacy-policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":392,"name":"Str\u00f6er Mobile Performance GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[3],"policyUrl":"https://stroeermobileperformance.com/?dl=privacy"},{"id":357,"name":"Totaljobs Group Ltd ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.totaljobs.com/privacy-policy"},{"id":486,"name":"Madington","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://delivered-by-madington.com/dat-privacy-policy/"},{"id":468,"name":"NeuStar, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://www.home.neustar/privacy"},{"id":458,"name":"AdColony, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"adcolony.com/privacy-policy/"},{"id":489,"name":"YellowHammer Media Group","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.yhmg.com/privacy-policy/","deletedDate":"2019-11-27T00:00:00Z"},{"id":293,"name":"SpringServe, LLC","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://springserve.com/privacy-policy/"},{"id":484,"name":"STRIATUM SAS","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://adledge.com/data-privacy/"},{"id":493,"name":"Carbon (AI) Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://carbonrmp.com/privacy.html"},{"id":495,"name":"Arcspire Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://public.arcspire.io/privacy.pdf"},{"id":496,"name":"Automattic Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://en.blog.wordpress.com/2017/12/04/updated-privacy-policy/"},{"id":424,"name":"KUPONA GmbH","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.kupona.de/dsgvo/"},{"id":408,"name":"Fidelity Media","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://fidelity-media.com/privacy-policy/"},{"id":473,"name":"Sub2 Technologies Ltd","purposeIds":[3,4,5],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.sub2tech.com/privacy-policy/"},{"id":467,"name":"Haensel AMS GmbH","purposeIds":[3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://haensel-ams.com/data-privacy/"},{"id":490,"name":"PLAYGROUND XYZ EMEA LTD","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://playground.xyz/privacy"},{"id":464,"name":"Oracle AddThis","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.addthis.com/privacy/privacy-policy/","deletedDate":"2020-02-12T00:00:00Z"},{"id":491,"name":"Triboo Data Analytics","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shinystat.com/it/informativa_privacy_generale.html"},{"id":499,"name":"PurposeLab, LLC","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://purposelab.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":502,"name":"NEXD","purposeIds":[5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://nexd.com/privacy-policy"},{"id":465,"name":"Schibsted Product and Tech UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.schibsted.com/","deletedDate":"2019-07-26T00:00:00Z"},{"id":497,"name":"Little Big Data sp.z.o.o.","purposeIds":[1,2,4],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://dtxngr.com/legal/"},{"id":492,"name":"LotaData, Inc.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1],"policyUrl":"https://lotadata.com/privacy_policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":512,"name":"PubNative GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://pubnative.net/privacy-notice/"},{"id":471,"name":"FlexOffers.com, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.flexoffers.com/privacy-policy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":494,"name":"Cablato Limited","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://cablato.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":516,"name":"Pexi B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://pexi.nl/privacy-policy/"},{"id":507,"name":"AdsWizz Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://www.adswizz.com/our-privacy-policy/"},{"id":482,"name":"UberMedia, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ubermedia.com/summary-of-privacy-policy/"},{"id":505,"name":"Shopalyst Inc","purposeIds":[1,2],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shortlyst.com/eu/privacy_terms.html"},{"id":517,"name":"SunMedia ","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2],"policyUrl":"https://www.sunmedia.tv/en/cookies"},{"id":518,"name":"Accelerize Inc.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://getcake.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":511,"name":"Admixer EU GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://admixer.com/privacy/"},{"id":479,"name":"INFINIA MOBILE S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.infiniamobile.com/privacy_policy"},{"id":513,"name":"Shopstyle","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shopstyle.co.uk/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":509,"name":"ATG Ad Tech Group GmbH","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ad-tech-group.com/privacy-policy/"},{"id":521,"name":"netzeffekt GmbH","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.netzeffekt.de/en/imprint"},{"id":487,"name":"nugg.ad GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1],"policyUrl":"https://www.nugg.ad/en/privacy/general-information.html","deletedDate":"2019-10-03T00:00:00Z"},{"id":515,"name":"ZighZag","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zighzag.com/privacy"},{"id":520,"name":"ChannelSight ","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.channelsight.com/privacypolicy/"},{"id":524,"name":"The Ozone Project Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://ozoneproject.com/privacy-policy"},{"id":529,"name":"Fidzup","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.fidzup.com/en/privacy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":528,"name":"Kayzen","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://kayzen.io/data-privacy-policy"},{"id":527,"name":"Jampp LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://jampp.com/privacy.html"},{"id":506,"name":"salesforce.com, inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.salesforce.com/company/privacy/"},{"id":534,"name":"SmartyAds Inc.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://smartyads.com/privacy-policy"},{"id":535,"name":"INNITY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.innity.com/privacy-policy.php"},{"id":514,"name":"Uprival LLC","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://uprival.com/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":522,"name":"Tealium Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://tealium.com/privacy-policy/"},{"id":530,"name":"Near Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://near.co/privacy"},{"id":539,"name":"AdDefend GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.addefend.com/en/privacy-policy/"},{"id":501,"name":"Alliance Gravity Data Media","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.alliancegravity.com/politiquedeprotectiondesdonneespersonnelles"},{"id":519,"name":"Chargeads","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.chargeplatform.com/privacy"},{"id":523,"name":"X-Mode Social, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://xmode.io/privacy-policy.html"},{"id":537,"name":"RUN, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.runads.com/privacy-policy"},{"id":531,"name":"Smartclip Hispania SL","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://rgpd-smartclip.com/"},{"id":536,"name":"GlobalWebIndex","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"http://legal.trendstream.net/non-panellist_privacy_policy"},{"id":542,"name":"Densou Trading Desk ApS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://densou.dk/Policy.html","deletedDate":"2020-01-21T00:00:00Z"},{"id":525,"name":"PUB OCEAN LIMITED","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://rta.pubocean.com/privacy-policy/","deletedDate":"2019-10-03T00:00:00Z"},{"id":544,"name":"Kochava Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://www.kochava.com/support-privacy/"},{"id":543,"name":"PaperG, Inc. dba Thunder Industries","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.makethunder.com/privacy"},{"id":334,"name":"Cydersoft","purposeIds":[],"legIntPurposeIds":[1,2,3,4],"featureIds":[2,3],"policyUrl":"http://www.videmob.com/privacy.html"},{"id":551,"name":"Illuma Technology Limited","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.weareilluma.com/endddd","deletedDate":"2019-11-14T00:00:00Z"},{"id":540,"name":"Tunnl BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://tunnl.com/privacy.html","deletedDate":"2019-12-20T00:00:00Z"},{"id":547,"name":"Video Reach","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.videoreach.de/about/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":546,"name":"Smart Traffik","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://okube-attribution.com/politique-de-confidentialite/"},{"id":541,"name":"DeepIntent, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.deepintent.com/privacypolicy"},{"id":545,"name":"Reignn Platform Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://reignn.com/user-privacy-policy"},{"id":439,"name":"Bit Q Holdings Limited","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.rippll.com/privacy"},{"id":553,"name":"Adhese","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://adhese.com/privacy-and-cookie-policy"},{"id":556,"name":"adhood.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://v3.adhood.com/en/site/politikavekurallar/gizlilik.php?lang=en"},{"id":550,"name":"Happydemics","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.iubenda.com/privacy-policy/69056167/full-legal"},{"id":560,"name":"Leiki Ltd.","purposeIds":[1,2,3],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"http://www.leiki.com/privacy","deletedDate":"2020-01-07T00:00:00Z"},{"id":554,"name":"RMSi Radio Marketing Service interactive GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.rms.de/datenschutz/"},{"id":498,"name":"Dr. Banner","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://drbanner.com/privacypolicy_en/"},{"id":565,"name":"Adobe Audience Manager","purposeIds":[1,2,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adobe.com/privacy/policy.html"},{"id":118,"name":"Drawbridge, Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.drawbridge.com/privacy/","deletedDate":"2020-03-06T00:00:00Z"},{"id":572,"name":"CHEQ AI TECHNOLOGIES LTD.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.cheq.ai/privacy"},{"id":571,"name":"ViewPay","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://viewpay.tv/mentions-legales/"},{"id":568,"name":"Jointag S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.jointag.com/privacy/kariboo/publisher/third/"},{"id":570,"name":"Czech Publisher Exchange z.s.p.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cpex.cz/pro-uzivatele/ochrana-soukromi/"},{"id":559,"name":"Otto (GmbH & Co KG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2],"policyUrl":"https://www.otto.de/shoppages/service/datenschutz"},{"id":548,"name":"LBC France","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.leboncoin.fr/dc/cookies","deletedDate":"2020-04-23T00:00:00Z"},{"id":569,"name":"Kairos Fire","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.kairosfire.com/privacy"},{"id":577,"name":"Neustar on behalf of The Procter & Gamble Company","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pg.com/privacy/english/privacy_statement.shtml"},{"id":590,"name":"Sourcepoint Technologies, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.sourcepoint.com/privacy-policy"},{"id":587,"name":"Localsensor B.V.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.localsensor.com/privacy.html"},{"id":578,"name":"MAIRDUMONT NETLETIX GmbH&Co. KG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mairdumont-netletix.com/datenschutz"},{"id":580,"name":"Goldbach Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://goldbach.com/ch/de/datenschutz"},{"id":593,"name":"Programatica de publicidad S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://datmean.com/politica-privacidad/"},{"id":574,"name":"Realeyes OU","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://realview.realeyesit.com/privacy"},{"id":581,"name":"Mobilewalla, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.mobilewalla.com/business-services-privacy-policy"},{"id":598,"name":"audio content & control GmbH","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://www.audio-cc.com/audiocc_privacy_policy.pdf"},{"id":596,"name":"InsurAds Technologies SA.","purposeIds":[3],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.insurads.com/privacy.html"},{"id":576,"name":"StartApp Inc.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://www.startapp.com/policy/privacy-policy/","deletedDate":"2020-04-23T00:00:00Z"},{"id":592,"name":"Colpirio.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy-policy.colpirio.com/en/","deletedDate":"2020-03-18T00:00:00Z"},{"id":549,"name":"Bandsintown Amplified LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://corp.bandsintown.com/privacy"},{"id":597,"name":"Better Banners A/S","purposeIds":[],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://betterbanners.com/en/privacy"},{"id":601,"name":"WebAds B.V","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.webads.eu/"},{"id":599,"name":"Maximus Live LLC","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://maximusx.com/privacy-policy/"},{"id":604,"name":"Join","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.teamjoin.fr/privacy.html","deletedDate":"2020-04-23T00:00:00Z"},{"id":606,"name":"Impactify ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://impactify.io/privacy-policy/"},{"id":608,"name":"News and Media Holding, a.s.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.newsandmedia.sk/gdpr/"},{"id":602,"name":"Online Solution Int Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://adsafety.net/privacy.html"},{"id":612,"name":"Adnami Aps","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adnami.io/privacy","deletedDate":"2020-03-17T00:00:00Z"},{"id":591,"name":"Consumable, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://consumable.com/privacy-policy.html"},{"id":614,"name":"Market Resource Partners LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.mrpfd.com/privacy-policy/"},{"id":615,"name":"Adsolutions BV","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adsolutions.com/privacy-policy/"},{"id":607,"name":"ucfunnel Co., Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.ucfunnel.com/privacy-policy"},{"id":609,"name":"Predicio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.predic.io/privacy"},{"id":617,"name":"Onfocus (Adagio)","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adagio.io/privacy"},{"id":620,"name":"Blue","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.getblue.io/privacy/"},{"id":610,"name":"Azerion Holding B.V.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://azerion.com/business/privacy.html"},{"id":621,"name":"Seznam.cz, a.s.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://www.seznam.cz/ochranaudaju"},{"id":624,"name":"Norstat AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.norstatpanel.com/en/data-protection"},{"id":623,"name":"Adprime Media Inc. ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adprimehealth.com/privacy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":95,"name":"Lotame Solutions, inc","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[2],"policyUrl":"https://www.lotame.com/about-lotame/privacy/lotame-corporate-websites-privacy-policy/"},{"id":618,"name":"BEINTOO SPA","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.beintoo.com/privacy-cookie-policy/"},{"id":619,"name":"Capitaldata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.capitaldata.fr/privacy"},{"id":625,"name":"BILENDI SA","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.maximiles.com/privacy-policy"},{"id":628,"name":": Tappx","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.tappx.com/en/privacy-policy/"},{"id":626,"name":"Hivestack Inc.","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://hivestack.com/privacy-policy"},{"id":631,"name":"Relay42 Netherlands B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://relay42.com/privacy"},{"id":627,"name":"D-Edge","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.d-edge.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":644,"name":"Gamoshi LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.gamoshi.com/privacy-policy"},{"id":639,"name":"Smile Wanted Group","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.smilewanted.com/privacy.php"},{"id":635,"name":"WebMediaRM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webmediarm.com/vie_privee_et_opposition_en.php"},{"id":579,"name":"Ve Global","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.ve.com/privacy-policy"},{"id":645,"name":"Noster Finance S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.finect.com/terminos-legales/politica-de-cookies"},{"id":653,"name":"Smartme Analytics","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"http://smartmeapp.com/info/smartme/aviso_legal.php","deletedDate":"2020-07-03T00:00:00Z"},{"id":613,"name":"Adserve.zone / Artworx AS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adserve.zone/adserveprivacypolicy.html"},{"id":573,"name":"Dailymotion SA","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2],"policyUrl":"https://www.dailymotion.com/legal/privacy"},{"id":652,"name":"Skaze","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.skaze.fr/rgpd/"},{"id":646,"name":"Notify","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"https://notify-group.com/en/mentions-legales/"},{"id":648,"name":"TrueData Solutions, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.truedata.co/privacy-policy/"},{"id":647,"name":"Axel Springer Teaser Ad GmbH","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://www.adup-tech.com/privacy"},{"id":654,"name":"GRAPHINIUM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.graphinium.com/privacy/"},{"id":659,"name":"Research and Analysis of Media in Sweden AB","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www2.rampanel.com/privacy-policy/"},{"id":656,"name":"Think Clever Media","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.contentignite.com/privacy-policy/"},{"id":504,"name":"Alive & Kicking Global Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mcsaatchiplc.com/legal/privacy-cookies","deletedDate":"2020-07-27T00:00:00Z"},{"id":657,"name":"GP One GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.gsi-one.org/de/privacy-policy.html"},{"id":655,"name":"Sportradar AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sportradar.com/about-us/privacy/"},{"id":662,"name":"SoundCast","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://soundcast.fm/en/data-privacy"},{"id":665,"name":"Digital East GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.digitaleast.mobi/en/legal/privacy-policy/"},{"id":650,"name":"Telefonica Investigaci\u00f3n y Desarrollo S.A.U","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.cognitivemarketing.tid.es/"},{"id":666,"name":"BeOp","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://beop.io/privacy"},{"id":663,"name":"Mobsuccess","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.mobsuccess.com/en/privacy"},{"id":658,"name":"BLIINK SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://bliink.io/privacy-policy"},{"id":667,"name":"Liftoff Mobile, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://liftoff.io/privacy-policy/"},{"id":668,"name":"WhatRocks Inc. ","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.whatrocks.co/en/privacy-policy "},{"id":670,"name":"Timehop, Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.timehop.com/privacy"},{"id":674,"name":"Duration Media, LLC.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.durationmedia.net/privacy-policy"},{"id":675,"name":"Instreamatic inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://instreamatic.com/privacy-policy/"},{"id":676,"name":"BusinessClick","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.businessclick.com/documents/RegulaminProgramuBusinessClick-2019.pdf"},{"id":677,"name":"Intercept Interactive Inc. dba Undertone","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.undertone.com/privacy/"},{"id":660,"name":"Schibsted Norge AS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://static.vg.no/privacy/","deletedDate":"2019-09-16T00:00:00Z"},{"id":673,"name":"TTNET AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.programattik.com/en/privacy-policy.aspx"},{"id":664,"name":"adMarketplace, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.admarketplace.com/privacy-policy/"},{"id":671,"name":"Mediaforce LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://casino.mindthebet.co.uk/themes/mindthebetv2-casino/privacy.php"},{"id":561,"name":"AuDigent","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://audigent.com/platform-privacy-policy"},{"id":682,"name":"Radio Net Media Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.adtonos.com/service-privacy-policy/"},{"id":684,"name":"Blue Billywig BV","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.bluebillywig.com/privacy-statement/"},{"id":686,"name":"The MediaGrid Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.themediagrid.com/privacy-policy/"},{"id":685,"name":"Arkeero","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://arkeero.com/privacy-2/"},{"id":687,"name":"MISSENA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://missena.com/confidentialite/"},{"id":690,"name":"Go.pl sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://go.pl/polityka-prywatnosci/"},{"id":691,"name":"Lifesight Pte. Ltd.","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.lifesight.io/privacy-policy/"},{"id":697,"name":"ADWAYS SAS","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.adways.com/confidentialite/?lang=en"},{"id":681,"name":"MyTraffic","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mytraffic.io/en/privacy"},{"id":649,"name":"adality GmbH","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[1],"policyUrl":"https://adality.de/en/privacy/"},{"id":712,"name":"Inspired Mobile Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://byinspired.com/privacypolicy.pdf"},{"id":688,"name":"Effinity","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.effiliation.com/politique-de-confidentialite/"},{"id":702,"name":"Kwanko","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.kwanko.com/fr/rgpd/"},{"id":715,"name":"BidBerry SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.bidberrymedia.com/privacy-policy/"},{"id":713,"name":"Dataseat Ltd","purposeIds":[2,5],"legIntPurposeIds":[1,3,4],"featureIds":[],"policyUrl":"https://dataseat.com/privacy-policy"},{"id":716,"name":"OnAudience Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.onaudience.com/internet-advertising-privacy-policy"},{"id":708,"name":"Dugout Limited ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://dugout.com/privacy-policy"},{"id":717,"name":"Audience Network","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.en.audiencenetwork.pl/internet-advertising-privacy-policy"},{"id":718,"name":"AppConsent Xchange","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://appconsent.io/en/privacy-policy"},{"id":720,"name":"AAX LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://aax.media/privacy/"},{"id":678,"name":"Axonix LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://axonix.com/privacy-cookie-policy/"},{"id":719,"name":"Online Advertising Network Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.oan.pl/en/privacy-policy"},{"id":707,"name":"Dentsu Aegis Network Italia SpA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.dentsuaegisnetwork.com/it/it/policies/info-cookie"},{"id":721,"name":"Beaconspark Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1],"policyUrl":"https://www.engageya.com/privacy"},{"id":724,"name":"Between Exchange","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"https://en.betweenx.com/pdata.pdf"},{"id":728,"name":"Appier PTE Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.appier.com/privacy-policy/"},{"id":729,"name":"Cavai AS & UK ","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://cav.ai/privacy-policy/"},{"id":723,"name":"Adzymic Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.adzymic.co/privacy"},{"id":737,"name":"Monet Engine Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appmonet.com/privacy-policy/"},{"id":740,"name":"6Sense Insights, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://6sense.com/privacy-policy/"},{"id":744,"name":"Vidazoo Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[2],"policyUrl":"https://vidazoo.gitbook.io/vidazoo-legal/privacy-policy"},{"id":731,"name":"GeistM Technologies LTD","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.geistm.com/privacy"},{"id":741,"name":"Brand Advance Limited","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.wearebrandadvance.com/website-privacy-policy"},{"id":734,"name":"Cint AB","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.cint.com/participant-privacy-notice"},{"id":709,"name":"NC Audience Exchange, LLC (NewsIQ)","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.ncaudienceexchange.com/privacy/"},{"id":739,"name":"Blingby LLC","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://blingby.com/privacy"},{"id":732,"name":"Performax.cz, s.r.o.","purposeIds":[2,4,5],"legIntPurposeIds":[1,3],"featureIds":[2,3],"policyUrl":"https://reg.tiscali.cz/privacy-policy"},{"id":736,"name":"BidMachine Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://explorestack.com/privacy-policy/"},{"id":738,"name":"adbility media GmbH","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adbility-media.com/datenschutzerklaerung/"},{"id":742,"name":"Audiencerate LTD","purposeIds":[],"legIntPurposeIds":[1,2,5],"featureIds":[],"policyUrl":"https://www.audiencerate.com/privacy/"},{"id":743,"name":"MOVIads Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://moviads.pl/polityka-prywatnosci/"},{"id":746,"name":"Adxperience SAS","purposeIds":[2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://adxperience.com/privacy-policy/"},{"id":747,"name":"Kairion GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://kairion.de/datenschutzbestimmungen/"},{"id":748,"name":"AUDIOMOB LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.audiomob.io/privacy"},{"id":749,"name":"Good-Loop Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://doc.good-loop.com/policy/privacy-policy.html"},{"id":754,"name":"DistroScale, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.distroscale.com/privacy-policy/"},{"id":756,"name":"Fandom, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"https://www.fandom.com/privacy-policy"},{"id":758,"name":"GfK Netherlands B.V.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://gfkpanel.nl/privacy"},{"id":759,"name":"RevJet","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.revjet.com/privacy"},{"id":760,"name":"VEXPRO TECHNOLOGIES LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://onedash.com/privacy-policy.html"},{"id":761,"name":"Digiseg ApS","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://digiseg.io/privacy-center/"},{"id":763,"name":"Delidatax SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.delidatax.net/privacy.htm"},{"id":764,"name":"Lucidity","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://golucidity.com/privacy-policy/"},{"id":765,"name":"Grabit Interactive Media Inc dba KERV Interctive","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://kervit.com/privacy-policy/"},{"id":766,"name":"ADCELL | Firstlead GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.adcell.de/agb#sector_6"},{"id":768,"name":"Global Media & Entertainment Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://global.com/privacy-policy/"},{"id":770,"name":"MARKETPERF CORP","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.marketperf.com/assets/images/app/marketperf/pdf/privacy-policy.pdf"},{"id":773,"name":"360e-com Sp. z o.o.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.clickonometrics.com/optout/"},{"id":775,"name":"SelectMedia International LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selectmedia.asia/terms-and-privacy/"},{"id":778,"name":"Discover-Tech ltd","purposeIds":[2,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://discover-tech.io/dsp-privacy-policy/"},{"id":779,"name":"Adtarget Medya A.S.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtarget.com.tr/adtarget-privacy-policy-2020.pdf"},{"id":780,"name":"Aniview LTD","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.aniview.com/privacy-policy/"},{"id":781,"name":"FeedAd GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://feedad.com/privacy/"},{"id":784,"name":"Nubo LTD","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.recod3.com/privacypolicy.php"},{"id":786,"name":"TargetVideo GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.target-video.com/datenschutz/"},{"id":798,"name":"Adverticum cPlc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://adverticum.net/english/privacy-and-data-processing-information/"},{"id":803,"name":"Click Tech Limited","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[1],"policyUrl":"https://en.yeahmobi.com/html/privacypolicy/"}]} \ No newline at end of file From b5f89336da09398de6d723c026825ad4bcf1afba Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Wed, 12 Aug 2020 12:37:43 -0400 Subject: [PATCH 162/318] update to the latest go-gdpr release (#1436) --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a5b5a161cf4..108e383e743 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect - github.com/prebid/go-gdpr v0.8.2 + github.com/prebid/go-gdpr v0.8.3 github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 diff --git a/go.sum b/go.sum index 1ddab71332a..6da3f8898ba 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLM github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v0.0.0-20180318095312-2cac668e8456/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.0.0 h1:naDmySfoNg0nKS62/ujM6e71ZgM2AoVdaqGwMG0w18A= @@ -77,8 +78,8 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prebid/go-gdpr v0.8.2 h1:mN2jKYZZpJkCYFQB/nDTJoPpuGYblOYP2UUzOzRggII= -github.com/prebid/go-gdpr v0.8.2/go.mod h1:FPY0uxSrl9/Mz237LnPo3ge4aCG0wQ9FWf2b4WhwNn0= +github.com/prebid/go-gdpr v0.8.3 h1:rjCZNV0AdKygiGHpVhNB42usjEpTN3qidXUPB1yarb0= +github.com/prebid/go-gdpr v0.8.3/go.mod h1:TGzgqQDGKOVUkbqmY25K4uvcwMywSddXEaY4zUFiVBQ= github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= From 48c865cb0bc7596b6fdb001f4b992b519fa45575 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Wed, 12 Aug 2020 10:07:58 -0700 Subject: [PATCH 163/318] Video endpoint bid selection enhancements (#1419) Co-authored-by: Veronika Solovei --- endpoints/openrtb2/video_auction.go | 18 +++- endpoints/openrtb2/video_auction_test.go | 29 +++-- exchange/auction.go | 2 +- .../impcustomcachekeytest/multiImpVideo.json | 101 ++++++++++++++++++ .../multiImpVideoNoIncludeBidderKeys.json | 86 +++++++++++++++ exchange/targeting.go | 4 +- exchange/targeting_test.go | 6 +- 7 files changed, 229 insertions(+), 17 deletions(-) create mode 100644 exchange/impcustomcachekeytest/multiImpVideo.json create mode 100644 exchange/impcustomcachekeytest/multiImpVideoNoIncludeBidderKeys.json diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 18678be541c..49ba287610b 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -470,7 +470,7 @@ func buildVideoResponse(bidresponse *openrtb.BidResponse, podErrors []PodError) if err := json.Unmarshal(bid.Ext, &tempRespBidExt); err != nil { return nil, err } - if tempRespBidExt.Prebid.Targeting[string(openrtb_ext.HbVastCacheKey)] == "" { + if tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbVastCacheKey, seatBid.Seat)] == "" { continue } @@ -479,9 +479,9 @@ func buildVideoResponse(bidresponse *openrtb.BidResponse, podErrors []PodError) podId, _ := strconv.ParseInt(podNum, 0, 64) videoTargeting := openrtb_ext.VideoTargeting{ - HbPb: tempRespBidExt.Prebid.Targeting[string(openrtb_ext.HbpbConstantKey)], - HbPbCatDur: tempRespBidExt.Prebid.Targeting[string(openrtb_ext.HbCategoryDurationKey)], - HbCacheID: tempRespBidExt.Prebid.Targeting[string(openrtb_ext.HbVastCacheKey)], + HbPb: tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbpbConstantKey, seatBid.Seat)], + HbPbCatDur: tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbCategoryDurationKey, seatBid.Seat)], + HbCacheID: tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbVastCacheKey, seatBid.Seat)], } adPod := findAdPod(podId, adPods) @@ -519,6 +519,14 @@ func buildVideoResponse(bidresponse *openrtb.BidResponse, podErrors []PodError) return &openrtb_ext.BidResponseVideo{AdPods: adPods}, nil } +func formatTargetingKey(key openrtb_ext.TargetingKey, bidderName string) string { + fullKey := fmt.Sprintf("%s_%s", string(key), bidderName) + if len(fullKey) > exchange.MaxKeyLength { + return string(fullKey[0:exchange.MaxKeyLength]) + } + return fullKey +} + func findAdPod(podInd int64, pods []*openrtb_ext.AdPod) *openrtb_ext.AdPod { for _, pod := range pods { if pod.PodId == podInd { @@ -623,9 +631,9 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro targeting := openrtb_ext.ExtRequestTargeting{ PriceGranularity: priceGranularity, - IncludeWinners: true, IncludeBrandCategory: inclBrandCat, DurationRangeSec: durationRangeSec, + IncludeBidderKeys: true, } vastXml := openrtb_ext.ExtRequestPrebidCacheVAST{} diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index f29ac3bfed9..b15c6a7b47a 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -81,6 +81,10 @@ func TestVideoEndpointImpressionsDuration(t *testing.T) { t.Fatalf("The request never made it into the Exchange.") } + var extData openrtb_ext.ExtRequest + json.Unmarshal(ex.lastRequest.Ext, &extData) + assert.True(t, extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ") + assert.Len(t, ex.lastRequest.Imp, 22, "Incorrect number of impressions in request") assert.Equal(t, ex.lastRequest.Imp[0].ID, "1_0", "Incorrect impression id in request") assert.Equal(t, ex.lastRequest.Imp[0].Video.MaxDuration, int64(15), "Incorrect impression max duration in request") @@ -643,9 +647,9 @@ func TestVideoBuildVideoResponseMissedCacheForOneBid(t *testing.T) { bid2 := openrtb.Bid{} bid3 := openrtb.Bid{} - extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_123_30s","hb_size":"1x1","hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) - extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_456_30s","hb_size":"1x1","hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) - extBid3 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_406_30s","hb_size":"1x1"}}}`) + extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_123_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) + extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_456_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) + extBid3 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_406_30s","hb_size":"1x1"}}}`) bid1.Ext = extBid1 bids = append(bids, bid1) @@ -657,6 +661,7 @@ func TestVideoBuildVideoResponseMissedCacheForOneBid(t *testing.T) { bids = append(bids, bid3) seatBid.Bid = bids + seatBid.Seat = "appnexus" seatBids = append(seatBids, seatBid) openRtbBidResp.SeatBid = seatBids @@ -713,8 +718,8 @@ func TestVideoBuildVideoResponsePodErrors(t *testing.T) { bid1 := openrtb.Bid{} bid2 := openrtb.Bid{} - extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_123_30s","hb_size":"1x1","hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) - extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"17.00","hb_pb_cat_dur":"17.00_456_30s","hb_size":"1x1","hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) + extBid1 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_123_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) + extBid2 := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"17.00","hb_pb_cat_dur_appnex":"17.00_456_30s","hb_size":"1x1","hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"}}}`) bid1.Ext = extBid1 bids = append(bids, bid1) @@ -723,6 +728,7 @@ func TestVideoBuildVideoResponsePodErrors(t *testing.T) { bids = append(bids, bid2) seatBid.Bid = bids + seatBid.Seat = "appnexus" seatBids = append(seatBids, seatBid) openRtbBidResp.SeatBid = seatBids @@ -1107,6 +1113,16 @@ func TestCCPA(t *testing.T) { } } +func TestFormatTargetingKey(t *testing.T) { + res := formatTargetingKey(openrtb_ext.HbCategoryDurationKey, "appnexus") + assert.Equal(t, "hb_pb_cat_dur_appnex", res, "Tergeting key constructed incorrectly") +} + +func TestFormatTargetingKeyLongKey(t *testing.T) { + res := formatTargetingKey(openrtb_ext.HbpbConstantKey, "20.00") + assert.Equal(t, "hb_pb_20.00", res, "Tergeting key constructed incorrectly") +} + func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *pbsmetrics.Metrics, *mockAnalyticsModule) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) mockModule := &mockAnalyticsModule{} @@ -1205,9 +1221,10 @@ func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb if debugLog != nil && debugLog.Enabled { m.cache.called = true } - ext := []byte(`{"prebid":{"targeting":{"hb_bidder":"appnexus","hb_pb":"20.00","hb_pb_cat_dur":"20.00_395_30s","hb_size":"1x1", "hb_uuid":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video"},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`) + ext := []byte(`{"prebid":{"targeting":{"hb_bidder_appnexus":"appnexus","hb_pb_appnexus":"20.00","hb_pb_cat_dur_appnex":"20.00_395_30s","hb_size":"1x1", "hb_uuid_appnexus":"837ea3b7-5598-4958-8c45-8e9ef2bf7cc1"},"type":"video"},"bidder":{"appnexus":{"brand_id":1,"auction_id":7840037870526938650,"bidder_id":2,"bid_ad_type":1,"creative_info":{"video":{"duration":30,"mimes":["video\/mp4"]}}}}}`) return &openrtb.BidResponse{ SeatBid: []openrtb.SeatBid{{ + Seat: "appnexus", Bid: []openrtb.Bid{ {ID: "01", ImpID: "1_0", Ext: ext}, {ID: "02", ImpID: "1_1", Ext: ext}, diff --git a/exchange/auction.go b/exchange/auction.go index dcb73708df7..45e1422540e 100644 --- a/exchange/auction.go +++ b/exchange/auction.go @@ -130,7 +130,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, var customCacheKey string var catDur string useCustomCacheKey := false - if competitiveExclusion && isOverallWinner { + if competitiveExclusion && isOverallWinner || includeBidderKeys { // set custom cache key for winning bid when competitive exclusion applies catDur = bidCategory[topBidPerBidder.bid.ID] if len(catDur) > 0 { diff --git a/exchange/impcustomcachekeytest/multiImpVideo.json b/exchange/impcustomcachekeytest/multiImpVideo.json new file mode 100644 index 00000000000..7c1d7af02ea --- /dev/null +++ b/exchange/impcustomcachekeytest/multiImpVideo.json @@ -0,0 +1,101 @@ +{ + "bidRequest": { + "imp": [{ + "id": "1_0" + }, + { + "id": "1_1" + } + ] + }, + "pbsBids": [{ + "bid": { + "id": "apn_1_0", + "impid": "1_0", + "price": 12.00, + "nurl": "http://apn_1_0.com", + "cat": ["12.00_sports_30s"] + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "spotx_1_0", + "impid": "1_0", + "price": 20.00, + "nurl": "http://spotx_1_0.com", + "cat": ["20_news_30s"] + }, + "bidType": "video", + "bidder": "spotx" + }, { + "bid": { + "id": "apn_1_1", + "impid": "1_1", + "price": 18.00, + "nurl": "http://apn_1_1.com", + "cat": ["18_furniture_30s"] + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "spotx_1_1", + "impid": "1_1", + "price": 17.00, + "nurl": "http://spotx_1_1.com", + "cat": ["17_auto_30s"] + }, + "bidType": "video", + "bidder": "spotx" + }, { + "bid": { + "id": "rubicon_1_1", + "impid": "1_1", + "price": 17.50, + "nurl": "http://rubicon_1_1.com", + "cat": ["17_music_30s"] + }, + "bidType": "video", + "bidder": "rubicon" + }], + "expectedCacheables": [{ + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://apn_1_0.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "12.00_sports_30s" + }, { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://spotx_1_0.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "20_news_30s" + }, { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://apn_1_1.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "18_furniture_30s" + }, + { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://spotx_1_1.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "17_auto_30s" + }, + { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://rubicon_1_1.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "17_music_30s" + } + ], + "defaultTTLs": { + "banner": 300, + "video": 3600, + "audio": 1800, + "native": 300 + }, + "targetDataIncludeWinners": false, + "targetDataIncludeBidderKeys": true, + "targetDataIncludeCacheBids": false, + "targetDataIncludeCacheVast": true +} diff --git a/exchange/impcustomcachekeytest/multiImpVideoNoIncludeBidderKeys.json b/exchange/impcustomcachekeytest/multiImpVideoNoIncludeBidderKeys.json new file mode 100644 index 00000000000..0b8ee1415e3 --- /dev/null +++ b/exchange/impcustomcachekeytest/multiImpVideoNoIncludeBidderKeys.json @@ -0,0 +1,86 @@ +{ + "bidRequest": { + "imp": [{ + "id": "1_0" + }, + { + "id": "1_1" + } + ] + }, + "pbsBids": [{ + "bid": { + "id": "apn_1_0", + "impid": "1_0", + "price": 12.00, + "nurl": "http://apn_1_0.com", + "cat": ["12.00_sports_30s"] + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "spotx_1_0", + "impid": "1_0", + "price": 20.00, + "nurl": "http://spotx_1_0.com", + "cat": ["20_news_30s"] + }, + "bidType": "video", + "bidder": "spotx" + }, { + "bid": { + "id": "apn_1_1", + "impid": "1_1", + "price": 18.00, + "nurl": "http://apn_1_1.com", + "cat": ["18_furniture_30s"] + }, + "bidType": "video", + "bidder": "appnexus" + }, { + "bid": { + "id": "spotx_1_1", + "impid": "1_1", + "price": 17.00, + "nurl": "http://spotx_1_1.com", + "cat": ["17_auto_30s"] + }, + "bidType": "video", + "bidder": "spotx" + }, { + "bid": { + "id": "rubicon_1_1", + "impid": "1_1", + "price": 17.50, + "nurl": "http://rubicon_1_1.com", + "cat": ["17_music_30s"] + }, + "bidType": "video", + "bidder": "rubicon" + }], + "expectedCacheables": [ + { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://spotx_1_0.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "20_news_30s" + }, + { + "Type": "xml", + "TTLSeconds": 3660, + "Data": "\u003cVAST version=\"3.0\"\u003e\u003cAd\u003e\u003cWrapper\u003e\u003cAdSystem\u003eprebid.org wrapper\u003c/AdSystem\u003e\u003cVASTAdTagURI\u003e\u003c![CDATA[http://apn_1_1.com]]\u003e\u003c/VASTAdTagURI\u003e\u003cImpression\u003e\u003c/Impression\u003e\u003cCreatives\u003e\u003c/Creatives\u003e\u003c/Wrapper\u003e\u003c/Ad\u003e\u003c/VAST\u003e", + "Key": "18_furniture_30s" + } + ], + "defaultTTLs": { + "banner": 300, + "video": 3600, + "audio": 1800, + "native": 300 + }, + "targetDataIncludeWinners": true, + "targetDataIncludeBidderKeys": false, + "targetDataIncludeCacheBids": false, + "targetDataIncludeCacheVast": true +} diff --git a/exchange/targeting.go b/exchange/targeting.go index dca57653b44..47bfeb655fe 100644 --- a/exchange/targeting.go +++ b/exchange/targeting.go @@ -7,7 +7,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) -const maxKeyLength = 20 +const MaxKeyLength = 20 // targetData tracks information about the winning Bid in each Imp. // @@ -83,7 +83,7 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool) { if targData.includeBidderKeys { - keys[key.BidderKey(bidderName, maxKeyLength)] = value + keys[key.BidderKey(bidderName, MaxKeyLength)] = value } if targData.includeWinners && overallWinner { keys[string(key)] = value diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 11b60db01f3..284d56be42e 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -50,13 +50,13 @@ func TestTargetingCache(t *testing.T) { // Make sure that the cache keys exist on the bids where they're expected to assertKeyExists(t, bids["winning-bid"], string(openrtb_ext.HbCacheKey), true) - assertKeyExists(t, bids["winning-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderAppnexus, maxKeyLength), true) + assertKeyExists(t, bids["winning-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderAppnexus, MaxKeyLength), true) assertKeyExists(t, bids["contending-bid"], string(openrtb_ext.HbCacheKey), false) - assertKeyExists(t, bids["contending-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderRubicon, maxKeyLength), true) + assertKeyExists(t, bids["contending-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderRubicon, MaxKeyLength), true) assertKeyExists(t, bids["losing-bid"], string(openrtb_ext.HbCacheKey), false) - assertKeyExists(t, bids["losing-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderAppnexus, maxKeyLength), false) + assertKeyExists(t, bids["losing-bid"], openrtb_ext.HbCacheKey.BidderKey(openrtb_ext.BidderAppnexus, MaxKeyLength), false) //assert hb_cache_host was included assert.Contains(t, string(bids["winning-bid"].Ext), string(openrtb_ext.HbConstantCacheHostKey)) From cce496720f13d30d45154ca7d80e81884fb9a1ac Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Wed, 12 Aug 2020 10:19:18 -0700 Subject: [PATCH 164/318] [WIP] Bid deduplication enhancement (#1430) Co-authored-by: Veronika Solovei --- exchange/exchange.go | 29 ++++++++++-- exchange/exchange_test.go | 96 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 9 deletions(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index ad591f57794..57e13644163 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -10,6 +10,7 @@ import ( "net/http" "runtime/debug" "sort" + "strconv" "strings" "time" @@ -484,6 +485,7 @@ func applyCategoryMapping(ctx context.Context, requestExt *openrtb_ext.ExtReques bidderName openrtb_ext.BidderName bidIndex int bidID string + bidPrice string } dedupe := make(map[string]bidDedupe) @@ -580,15 +582,34 @@ func applyCategoryMapping(ctx context.Context, requestExt *openrtb_ext.ExtReques } var categoryDuration string + var dupeKey string if brandCatExt.WithCategory { categoryDuration = fmt.Sprintf("%s_%s_%ds", pb, category, newDur) + dupeKey = category } else { categoryDuration = fmt.Sprintf("%s_%ds", pb, newDur) + dupeKey = categoryDuration } - if dupe, ok := dedupe[categoryDuration]; ok { - // 50% chance for either bid with duplicate categoryDuration values to be kept - if rand.Intn(100) < 50 { + if dupe, ok := dedupe[dupeKey]; ok { + + dupeBidPrice, err := strconv.ParseFloat(dupe.bidPrice, 64) + if err != nil { + dupeBidPrice = 0 + } + currBidPrice, err := strconv.ParseFloat(pb, 64) + if err != nil { + currBidPrice = 0 + } + if dupeBidPrice == currBidPrice { + if rand.Intn(100) < 50 { + dupeBidPrice = -1 + } else { + currBidPrice = -1 + } + } + + if dupeBidPrice < currBidPrice { if dupe.bidderName == bidderName { // An older bid from the current bidder bidsToRemove = append(bidsToRemove, dupe.bidIndex) @@ -612,7 +633,7 @@ func applyCategoryMapping(ctx context.Context, requestExt *openrtb_ext.ExtReques } } res[bidID] = categoryDuration - dedupe[categoryDuration] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID} + dedupe[dupeKey] = bidDedupe{bidderName: bidderName, bidIndex: bidInd, bidID: bidID, bidPrice: pb} } if len(bidsToRemove) > 0 { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 7da7b62e70b..5fbdb1c57a9 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1352,19 +1352,22 @@ func TestCategoryDedupe(t *testing.T) { cats4 := []string{"IAB1-2000"} bid1 := openrtb.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1} bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 15.0000, Cat: cats2, W: 1, H: 1} - bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 10.0000, Cat: cats1, W: 1, H: 1} + bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 20.0000, Cat: cats1, W: 1, H: 1} bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1} + bid5 := openrtb.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 20.0000, Cat: cats1, W: 1, H: 1} bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, 0} bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} selectedBids := make(map[string]int) expectedCategories := map[string]string{ "bid_id1": "10.00_Electronics_30s", "bid_id2": "14.00_Sports_50s", - "bid_id3": "10.00_Electronics_30s", + "bid_id3": "20.00_Electronics_30s", + "bid_id5": "20.00_Electronics_30s", } numIterations := 10 @@ -1378,6 +1381,7 @@ func TestCategoryDedupe(t *testing.T) { &bid1_2, &bid1_3, &bid1_4, + &bid1_5, } seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} @@ -1388,7 +1392,7 @@ func TestCategoryDedupe(t *testing.T) { bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) assert.Equal(t, nil, err, "Category mapping error should be empty") - assert.Equal(t, 2, len(rejections), "There should be 2 bid rejection messages") + assert.Equal(t, 3, len(rejections), "There should be 2 bid rejection messages") assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_id(1|3)\] reason: Bid was deduplicated`), rejections[0], "Rejection message did not match expected") assert.Equal(t, "bid rejected [bid ID: bid_id4] reason: Category mapping file for primary ad server: 'freewheel', publisher: '' not found", rejections[1], "Rejection message did not match expected") assert.Equal(t, 2, len(adapterBids[bidderName1].bids), "Bidders number doesn't match") @@ -1401,8 +1405,90 @@ func TestCategoryDedupe(t *testing.T) { } assert.Equal(t, numIterations, selectedBids["bid_id2"], "Bid 2 did not make it through every time") - assert.NotEqual(t, numIterations, selectedBids["bid_id1"], "Bid 1 made it through every time") - assert.NotEqual(t, numIterations, selectedBids["bid_id3"], "Bid 3 made it through every time") + assert.Equal(t, 0, selectedBids["bid_id1"], "Bid 1 should be rejected on every iteration due to lower price") + assert.NotEqual(t, 0, selectedBids["bid_id3"], "Bid 3 should be accepted at least once") + assert.NotEqual(t, 0, selectedBids["bid_id5"], "Bid 5 should be accepted at least once") +} + +func TestNoCategoryDedupe(t *testing.T) { + + categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") + if error != nil { + t.Errorf("Failed to create a category Fetcher: %v", error) + } + + requestExt := newExtRequestNoBrandCat() + + targData := &targetData{ + priceGranularity: requestExt.Prebid.Targeting.PriceGranularity, + includeWinners: true, + } + + adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid) + + cats1 := []string{"IAB1-3"} + cats2 := []string{"IAB1-4"} + cats4 := []string{"IAB1-2000"} + bid1 := openrtb.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 14.0000, Cat: cats1, W: 1, H: 1} + bid2 := openrtb.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 14.0000, Cat: cats2, W: 1, H: 1} + bid3 := openrtb.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 20.0000, Cat: cats1, W: 1, H: 1} + bid4 := openrtb.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1} + bid5 := openrtb.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 10.0000, Cat: cats1, W: 1, H: 1} + + bid1_1 := pbsOrtbBid{&bid1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_2 := pbsOrtbBid{&bid2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_3 := pbsOrtbBid{&bid3, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_4 := pbsOrtbBid{&bid4, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_5 := pbsOrtbBid{&bid5, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + + selectedBids := make(map[string]int) + expectedCategories := map[string]string{ + "bid_id1": "14.00_30s", + "bid_id2": "14.00_30s", + "bid_id3": "20.00_30s", + "bid_id4": "20.00_30s", + "bid_id5": "10.00_30s", + } + + numIterations := 10 + + // Run the function many times, this should be enough for the 50% chance of which bid to remove to remove bid1 sometimes + // and bid3 others. It's conceivably possible (but highly unlikely) that the same bid get chosen every single time, but + // if you notice false fails from this test increase numIterations to make it even less likely to happen. + for i := 0; i < numIterations; i++ { + innerBids := []*pbsOrtbBid{ + &bid1_1, + &bid1_2, + &bid1_3, + &bid1_4, + &bid1_5, + } + + seatBid := pbsOrtbSeatBid{innerBids, "USD", nil, nil} + bidderName1 := openrtb_ext.BidderName("appnexus") + + adapterBids[bidderName1] = &seatBid + + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) + + assert.Equal(t, nil, err, "Category mapping error should be empty") + assert.Equal(t, 2, len(rejections), "There should be 2 bid rejection messages") + assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_id(1|2)\] reason: Bid was deduplicated`), rejections[0], "Rejection message did not match expected") + assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_id(3|4)\] reason: Bid was deduplicated`), rejections[1], "Rejection message did not match expected") + assert.Equal(t, 3, len(adapterBids[bidderName1].bids), "Bidders number doesn't match") + assert.Equal(t, 3, len(bidCategory), "Bidders category mapping doesn't match") + + for bidId, bidCat := range bidCategory { + assert.Equal(t, expectedCategories[bidId], bidCat, "Category mapping doesn't match") + selectedBids[bidId]++ + } + } + assert.Equal(t, numIterations, selectedBids["bid_id5"], "Bid 5 did not make it through every time") + assert.NotEqual(t, 0, selectedBids["bid_id1"], "Bid 1 should be selected at least once") + assert.NotEqual(t, 0, selectedBids["bid_id2"], "Bid 2 should be selected at least once") + assert.NotEqual(t, 0, selectedBids["bid_id1"], "Bid 3 should be selected at least once") + assert.NotEqual(t, 0, selectedBids["bid_id4"], "Bid 4 should be selected at least once") + } func TestBidRejectionErrors(t *testing.T) { From 346617b0d08623e7976f18a63db28d2f8bd8bd1c Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Wed, 12 Aug 2020 13:58:40 -0400 Subject: [PATCH 165/318] Refactor rate converter separating scheduler from converter logic to improve testability (#1394) --- currencies/converter_info.go | 15 +- currencies/rate_converter.go | 120 +--- currencies/rate_converter_test.go | 676 ++++++------------- currencies/rates_test.go | 1 + endpoints/currency_rates.go | 7 +- endpoints/currency_rates_test.go | 51 +- endpoints/openrtb2/auction_benchmark_test.go | 3 +- exchange/bidder_test.go | 18 +- exchange/exchange_test.go | 25 +- exchange/legacy_test.go | 12 +- exchange/targeting_test.go | 2 +- main.go | 9 +- router/admin.go | 5 +- util/task/ticker_task.go | 53 ++ util/task/ticker_task_test.go | 63 ++ util/timeutil/time.go | 16 + 16 files changed, 431 insertions(+), 645 deletions(-) create mode 100644 util/task/ticker_task.go create mode 100644 util/task/ticker_task_test.go create mode 100644 util/timeutil/time.go diff --git a/currencies/converter_info.go b/currencies/converter_info.go index 6f4fda64c09..abbcde29fbc 100644 --- a/currencies/converter_info.go +++ b/currencies/converter_info.go @@ -5,18 +5,16 @@ import "time" // ConverterInfo holds information about converter setup type ConverterInfo interface { Source() string - FetchingInterval() time.Duration LastUpdated() time.Time Rates() *map[string]map[string]float64 AdditionalInfo() interface{} } type converterInfo struct { - source string - fetchingInterval time.Duration - lastUpdated time.Time - rates *map[string]map[string]float64 - additionalInfo interface{} + source string + lastUpdated time.Time + rates *map[string]map[string]float64 + additionalInfo interface{} } // Source returns converter's URL source @@ -24,11 +22,6 @@ func (ci converterInfo) Source() string { return ci.source } -// FetchingInterval returns converter's fetching interval in nanoseconds -func (ci converterInfo) FetchingInterval() time.Duration { - return ci.fetchingInterval -} - // LastUpdated returns converter's last updated time func (ci converterInfo) LastUpdated() time.Time { return ci.lastUpdated diff --git a/currencies/rate_converter.go b/currencies/rate_converter.go index d22f347b17c..f9ae67436f9 100644 --- a/currencies/rate_converter.go +++ b/currencies/rate_converter.go @@ -2,83 +2,43 @@ package currencies import ( "encoding/json" + "fmt" "io/ioutil" "net/http" "sync/atomic" "time" "github.com/golang/glog" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/util/timeutil" ) // RateConverter holds the currencies conversion rates dictionary type RateConverter struct { httpClient httpClient - done chan bool - updateNotifier chan<- int - fetchingInterval time.Duration staleRatesThreshold time.Duration syncSourceURL string rates atomic.Value // Should only hold Rates struct lastUpdated atomic.Value // Should only hold time.Time constantRates Conversions + time timeutil.Time } // NewRateConverter returns a new RateConverter func NewRateConverter( httpClient httpClient, syncSourceURL string, - fetchingInterval time.Duration, staleRatesThreshold time.Duration, ) *RateConverter { - return NewRateConverterWithNotifier( - httpClient, - syncSourceURL, - fetchingInterval, - staleRatesThreshold, - nil, // no notifier channel specified, won't send any notifications - ) -} - -// NewRateConverterDefault returns a RateConverter with default values. -// By default there will be no currencies conversions done. -// `currencies.ConstantRate` will be used. -func NewRateConverterDefault() *RateConverter { - return NewRateConverter(&http.Client{}, "", time.Duration(0), time.Duration(0)) -} - -// NewRateConverterWithNotifier returns a new RateConverter -// it allow to pass an update chan in which the number of ticks will be passed after each tick -// allowing clients to listen on updates -// Do not use this method -func NewRateConverterWithNotifier( - httpClient httpClient, - syncSourceURL string, - fetchingInterval time.Duration, - staleRatesThreshold time.Duration, - updateNotifier chan<- int, -) *RateConverter { - rc := &RateConverter{ + return &RateConverter{ httpClient: httpClient, - done: make(chan bool), - updateNotifier: updateNotifier, - fetchingInterval: fetchingInterval, staleRatesThreshold: staleRatesThreshold, syncSourceURL: syncSourceURL, rates: atomic.Value{}, lastUpdated: atomic.Value{}, constantRates: NewConstantRates(), + time: &timeutil.RealTime{}, } - - // In case host do not want to support currency lookup - // we just stop here and do nothing - if rc.fetchingInterval == time.Duration(0) { - return rc - } - - rc.Update() // Make sure to populate data before to return the converter - go rc.startPeriodicFetching() // Start periodic ticking - - return rc } // fetch allows to retrieve the currencies rates from the syncSourceURL provided @@ -93,6 +53,11 @@ func (rc *RateConverter) fetch() (*Rates, error) { return nil, err } + if response.StatusCode >= 400 { + message := fmt.Sprintf("The currency rates request failed with status code %d", response.StatusCode) + return nil, &errortypes.BadServerResponse{Message: message} + } + defer response.Body.Close() bytesJSON, err := ioutil.ReadAll(response.Body) @@ -110,14 +75,14 @@ func (rc *RateConverter) fetch() (*Rates, error) { } // Update updates the internal currencies rates from remote sources -func (rc *RateConverter) Update() error { +func (rc *RateConverter) update() error { rates, err := rc.fetch() if err == nil { rc.rates.Store(rates) - rc.lastUpdated.Store(time.Now()) + rc.lastUpdated.Store(rc.time.Now()) } else { - if rc.CheckStaleRates() { - rc.ClearRates() + if rc.checkStaleRates() { + rc.clearRates() glog.Errorf("Error updating conversion rates, falling back to constant rates: %v", err) } else { glog.Errorf("Error updating conversion rates: %v", err) @@ -127,37 +92,8 @@ func (rc *RateConverter) Update() error { return err } -// startPeriodicFetching starts the periodic fetching at the given interval -// triggers a first fetch when called before the first tick happen in order to initialize currencies rates map -// returns a chan in which the number of data updates everytime a new update was done -func (rc *RateConverter) startPeriodicFetching() { - - ticker := time.NewTicker(rc.fetchingInterval) - updatesTicksCount := 0 - - for { - select { - case <-ticker.C: - // Retries are handled by clients directly. - rc.Update() - updatesTicksCount++ - if rc.updateNotifier != nil { - rc.updateNotifier <- updatesTicksCount - } - case <-rc.done: - if ticker != nil { - ticker.Stop() - ticker = nil - } - return - } - } -} - -// StopPeriodicFetching stops the periodic fetching while keeping the latest currencies rates map -func (rc *RateConverter) StopPeriodicFetching() { - rc.done <- true - close(rc.done) +func (rc *RateConverter) Run() error { + return rc.update() } // LastUpdated returns time when currencies rates were updated @@ -178,18 +114,19 @@ func (rc *RateConverter) Rates() Conversions { return rc.constantRates } -// ClearRates sets the rates to nil -func (rc *RateConverter) ClearRates() { +// clearRates sets the rates to nil +func (rc *RateConverter) clearRates() { // atomic.Value field rates must be of type *Rates so we cast nil to that type rc.rates.Store((*Rates)(nil)) } -// CheckStaleRates checks if loaded third party conversion rates are stale -func (rc *RateConverter) CheckStaleRates() bool { +// checkStaleRates checks if loaded third party conversion rates are stale +func (rc *RateConverter) checkStaleRates() bool { if rc.staleRatesThreshold <= 0 { return false } - currentTime := time.Now().UTC() + + currentTime := rc.time.Now().UTC() if lastUpdated := rc.lastUpdated.Load(); lastUpdated != nil { delta := currentTime.Sub(lastUpdated.(time.Time).UTC()) if delta.Seconds() > rc.staleRatesThreshold.Seconds() { @@ -202,14 +139,11 @@ func (rc *RateConverter) CheckStaleRates() bool { // GetInfo returns setup information about the converter func (rc *RateConverter) GetInfo() ConverterInfo { var rates *map[string]map[string]float64 - if rc.Rates() != nil { - rates = rc.Rates().GetRates() - } + rates = rc.Rates().GetRates() return converterInfo{ - source: rc.syncSourceURL, - fetchingInterval: rc.fetchingInterval, - lastUpdated: rc.LastUpdated(), - rates: rates, + source: rc.syncSourceURL, + lastUpdated: rc.LastUpdated(), + rates: rates, } } diff --git a/currencies/rate_converter_test.go b/currencies/rate_converter_test.go index d717d1a3f9c..7beb9523d28 100644 --- a/currencies/rate_converter_test.go +++ b/currencies/rate_converter_test.go @@ -1,4 +1,4 @@ -package currencies_test +package currencies import ( "io/ioutil" @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/currencies" + "github.com/prebid/prebid-server/util/task" "github.com/stretchr/testify/assert" ) @@ -27,423 +27,148 @@ func getMockRates() []byte { }`) } -func TestFetch_Success(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - }), - ) - - defer mockedHttpServer.Close() - - expectedRates := ¤cies.Rates{ - DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), - Conversions: map[string]map[string]float64{ - "USD": { - "GBP": 0.77208, - }, - "GBP": { - "USD": 1.2952, - }, - }, - } - - // Execute: - beforeExecution := time.Now() - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) - assert.NotEqual(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() should return a time set") - assert.True(t, currencyConverter.LastUpdated().After(beforeExecution), "LastUpdated() should be after last update") - rates := currencyConverter.Rates() - assert.NotNil(t, rates, "Rates() should not return nil") - assert.Equal(t, expectedRates, rates, "Rates() doesn't return expected rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") -} - -func TestFetch_Fail404(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusNotFound) - }), - ) - - defer mockedHttpServer.Close() - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) - assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") +// FakeTime implements the Time interface +type FakeTime struct { + time time.Time } -func TestFetch_FailErrorHttpClient(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusBadRequest) - }), - ) - - defer mockedHttpServer.Close() - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) - assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") +func (mc *FakeTime) Now() time.Time { + return mc.time } -func TestFetch_FailBadSyncURL(t *testing.T) { - - // Setup: - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - "justaweirdurl", - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") -} - -func TestFetch_FailBadJSON(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusOK) - rw.Write([]byte( - `{ - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - }, - "badJsonHere" - } - }`, - )) - }), - ) - - defer mockedHttpServer.Close() - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) - assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") -} - -func TestFetch_InvalidRemoteResponseContent(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusOK) - rw.Write(nil) - }), - ) - - defer mockedHttpServer.Close() - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(24)*time.Hour, - time.Duration(24)*time.Hour, - ) - - // Verify: - assert.Equal(t, 1, len(calledURLs), "sync URL should have been called %d times but was %d", 1, len(calledURLs)) - assert.Equal(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated() shouldn't return a time set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") -} - -func TestInit(t *testing.T) { - - // Setup: - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - }), - ) - - // Execute: - expectedTicks := 5 - ticksTimes := []time.Time{} - ticks := make(chan int) - currencyConverter := currencies.NewRateConverterWithNotifier( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(24)*time.Hour, - ticks, - ) +func TestReadWriteRates(t *testing.T) { + // Setup + mockServerHandler := func(mockResponse []byte, mockStatus int) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(mockStatus) + rw.Write([]byte(mockResponse)) + }) + } - // Verify: - expectedIntervalDuration := time.Duration(100) * time.Millisecond - errorMargin := 0.1 // 10% error margin - expectedRates := ¤cies.Rates{ - DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), - Conversions: map[string]map[string]float64{ - "USD": { - "GBP": 0.77208, - }, - "GBP": { - "USD": 1.2952, - }, + tests := []struct { + description string + giveFakeTime time.Time + giveMockUrl string + giveMockResponse []byte + giveMockStatus int + wantUpdateErr bool + wantConstantRates bool + wantLastUpdated time.Time + wantDataAsOf time.Time + wantConversions map[string]map[string]float64 + }{ + { + description: "Fetching currency rates successfully", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockResponse: getMockRates(), + giveMockStatus: 200, + wantLastUpdated: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + wantDataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), + wantConversions: map[string]map[string]float64{"USD": {"GBP": 0.77208}, "GBP": {"USD": 1.2952}}, + }, + { + description: "Currency rates endpoint returns empty response", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockResponse: []byte("{}"), + giveMockStatus: 200, + wantLastUpdated: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + wantDataAsOf: time.Time{}, + wantConversions: nil, + }, + { + description: "Currency rates endpoint returns nil response", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockResponse: nil, + giveMockStatus: 200, + wantUpdateErr: true, + wantConstantRates: true, + wantLastUpdated: time.Time{}, + }, + { + description: "Currency rates endpoint returns non-2xx status code", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockResponse: []byte(`{"message": "Not Found"}`), + giveMockStatus: 404, + wantUpdateErr: true, + wantConstantRates: true, + wantLastUpdated: time.Time{}, + }, + { + description: "Currency rates endpoint returns invalid json response", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockResponse: []byte(`{"message": Invalid-JSON-No-Surrounding-Quotes}`), + giveMockStatus: 200, + wantUpdateErr: true, + wantConstantRates: true, + wantLastUpdated: time.Time{}, + }, + { + description: "Currency rates endpoint url is invalid", + giveFakeTime: time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC), + giveMockUrl: "invalidurl", + giveMockResponse: getMockRates(), + giveMockStatus: 200, + wantUpdateErr: true, + wantConstantRates: true, + wantLastUpdated: time.Time{}, }, } - // At each ticks, do couple checks - for ticksCount := range ticks { - ticksTimes = append(ticksTimes, time.Now()) - if len(ticksTimes) > 1 { - intervalDuration := ticksTimes[len(ticksTimes)-1].Truncate(time.Millisecond).Sub(ticksTimes[len(ticksTimes)-2].Truncate(time.Millisecond)) - intervalDiff := float64(float64(intervalDuration.Nanoseconds()) / float64(expectedIntervalDuration.Nanoseconds())) - assert.False(t, intervalDiff > float64(errorMargin*100), "Interval between ticks should be: %d but was: %d", expectedIntervalDuration, intervalDuration) - } - - assert.NotEqual(t, currencyConverter.LastUpdated(), (time.Time{}), "LastUpdated should be set") - assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") + for _, tt := range tests { + mockedHttpServer := httptest.NewServer(mockServerHandler(tt.giveMockResponse, tt.giveMockStatus)) + defer mockedHttpServer.Close() - if ticksCount == expectedTicks { - currencyConverter.StopPeriodicFetching() - return + var url string + if len(tt.giveMockUrl) > 0 { + url = tt.giveMockUrl + } else { + url = mockedHttpServer.URL } - } -} - -func TestStop(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - }), - ) - - // Execute: - expectedTicks := 2 - ticks := make(chan int) - currencyConverter := currencies.NewRateConverterWithNotifier( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(24)*time.Hour, - ticks, - ) - - // Let the currency converter fetch 5 times before stopping it - for ticksCount := range ticks { - if ticksCount == expectedTicks { - currencyConverter.StopPeriodicFetching() - break + currencyConverter := NewRateConverter( + &http.Client{}, + url, + 24*time.Hour, + ) + currencyConverter.time = &FakeTime{time: tt.giveFakeTime} + err := currencyConverter.Run() + + if tt.wantUpdateErr { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) } - } - lastFetched := time.Now() - - // Verify: - // Check for the next 1 second that no fetch was triggered - time.Sleep(1 * time.Second) - - assert.False(t, currencyConverter.LastUpdated().After(lastFetched), "LastUpdated() shouldn't be after `lastFetched` since the periodic fetching is stopped") -} - -func TestInitWithZeroDuration(t *testing.T) { - - // Setup: - calledURLs := []string{} - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - }), - ) - - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(0), - time.Duration(24)*time.Hour, - ) - - // Verify: - // Check for the next 1 second that no fetch was triggered - time.Sleep(1 * time.Second) - - assert.Equal(t, 0, len(calledURLs), "sync URL shouldn't have been called but was called %d times", 0, len(calledURLs)) - assert.Equal(t, (time.Time{}), currencyConverter.LastUpdated(), "LastUpdated() shouldn't be set") - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") - assert.NotNil(t, currencyConverter.GetInfo(), "GetInfo() should not return nil") -} - -func TestRates(t *testing.T) { - - // Setup: - testCases := []struct { - from string - to string - expectedRate float64 - hasError bool - }{ - {from: "USD", to: "GBP", expectedRate: 0.77208, hasError: false}, - {from: "GBP", to: "USD", expectedRate: 1.2952, hasError: false}, - {from: "GBP", to: "EUR", expectedRate: 0, hasError: true}, - {from: "CNY", to: "EUR", expectedRate: 0, hasError: true}, - {from: "", to: "EUR", expectedRate: 0, hasError: true}, - {from: "CNY", to: "", expectedRate: 0, hasError: true}, - {from: "", to: "", expectedRate: 0, hasError: true}, - {from: "USD", to: "USD", expectedRate: 1, hasError: false}, - } - - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - }), - ) - - // Execute: - ticks := make(chan int) - currencyConverter := currencies.NewRateConverterWithNotifier( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(24)*time.Hour, - ticks, - ) - rates := currencyConverter.Rates() - // Let the currency converter ticks 1 time before to stop it - select { - case <-ticks: - currencyConverter.StopPeriodicFetching() - } - - // Verify: - assert.NotNil(t, rates, "rates shouldn't be nil") - for _, tc := range testCases { - rate, err := rates.GetRate(tc.from, tc.to) - - if tc.hasError { - assert.NotNil(t, err, "err shouldn't be nil") - assert.Equal(t, float64(0), rate, "rate should be 0") + if tt.wantConstantRates { + assert.Equal(t, currencyConverter.Rates(), &ConstantRates{}, tt.description) } else { - assert.Nil(t, err, "err should be nil") - assert.Equal(t, tc.expectedRate, rate, "rate doesn't match the expected one") + rates := currencyConverter.Rates().(*Rates) + assert.Equal(t, tt.wantConversions, (*rates).Conversions, tt.description) + assert.Equal(t, tt.wantDataAsOf, (*rates).DataAsOf, tt.description) } - } -} - -func TestRates_EmptyRates(t *testing.T) { - // Setup: - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - rw.Write([]byte("")) - }), - ) - - // Execute: - // Will try to fetch directly on method call but will fail - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(24)*time.Hour, - ) - defer currencyConverter.StopPeriodicFetching() - - // Verify: - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + lastUpdated := currencyConverter.LastUpdated() + assert.Equal(t, tt.wantLastUpdated, lastUpdated, tt.description) + } } -func TestSelectRatesBasedOnStaleness(t *testing.T) { - calledURLs := []string{} - callCnt := 0 +func TestRateStaleness(t *testing.T) { + callCount := 0 mockedHttpServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { - calledURLs = append(calledURLs, req.RequestURI) - if callCnt == 0 || callCnt >= 5 { + callCount++ + if callCount == 2 || callCount >= 5 { rw.WriteHeader(http.StatusOK) rw.Write([]byte(getMockRates())) } else { rw.WriteHeader(http.StatusNotFound) + rw.Write([]byte(`{"message": "Not Found"}`)) } - callCnt++ }), ) defer mockedHttpServer.Close() - expectedRates := ¤cies.Rates{ + expectedRates := &Rates{ DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), Conversions: map[string]map[string]float64{ "USD": { @@ -455,97 +180,83 @@ func TestSelectRatesBasedOnStaleness(t *testing.T) { }, } + initialFakeTime := time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC) + fakeTime := &FakeTime{time: initialFakeTime} + // Execute: - currencyConverter := currencies.NewRateConverter( + currencyConverter := NewRateConverter( &http.Client{}, mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(200)*time.Millisecond, + 30*time.Second, // stale rates threshold ) + currencyConverter.time = fakeTime - // Verify: - // Rates are valid at t=0, then invalid for 500ms before being valid again - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + // First Update call results in error + err1 := currencyConverter.Run() + assert.NotNil(t, err1) - time.Sleep(100 * time.Millisecond) - // Rates have been invalid for ~100ms, rates not stale yet - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + // Verify constant rates are used and last update ts is not set + assert.Equal(t, &ConstantRates{}, currencyConverter.Rates(), "Rates should return constant rates") + assert.Equal(t, time.Time{}, currencyConverter.LastUpdated(), "LastUpdated return is incorrect") - time.Sleep(200 * time.Millisecond) - // Rates have been invalid for ~300ms, rates are stale - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + // Second Update call is successful and yields valid rates + err2 := currencyConverter.Run() + assert.Nil(t, err2) - time.Sleep(300 * time.Millisecond) - // Rates have been valid again for ~100ms - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") -} + // Verify rates are valid and last update timestamp is set + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") + assert.Equal(t, initialFakeTime, currencyConverter.LastUpdated(), "LastUpdated should be set") -func TestUseConstantRatesUntilFetchIsSuccessful(t *testing.T) { - callCnt := 0 - mockedHttpServer := httptest.NewServer(http.HandlerFunc( - func(rw http.ResponseWriter, req *http.Request) { - if callCnt >= 5 { - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(getMockRates())) - } else { - rw.WriteHeader(http.StatusNotFound) - } - callCnt++ - }), - ) + // Advance time so the rates fall just short of being considered stale + fakeTime.time = fakeTime.time.Add(29 * time.Second) - defer mockedHttpServer.Close() + // Third Update call results in error but stale rate threshold has not been exceeded + err3 := currencyConverter.Run() + assert.NotNil(t, err3) - expectedRates := ¤cies.Rates{ - DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), - Conversions: map[string]map[string]float64{ - "USD": { - "GBP": 0.77208, - }, - "GBP": { - "USD": 1.2952, - }, - }, - } + // Verify rates are valid and last update ts has not changed + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") + assert.Equal(t, initialFakeTime, currencyConverter.LastUpdated(), "LastUpdated should be set") - // Execute: - currencyConverter := currencies.NewRateConverter( - &http.Client{}, - mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(1)*time.Second, - ) + // Advance time just past the threshold so the rates are considered stale + fakeTime.time = fakeTime.time.Add(2 * time.Second) + + // Fourth Update call results in error and stale rate threshold has been exceeded + err4 := currencyConverter.Run() + assert.NotNil(t, err4) - // Verify: - // Rates are invalid at t=0 and remain invalid until 500ms have elapsed - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + // Verify constant rates are used and last update ts has not changed + assert.Equal(t, &ConstantRates{}, currencyConverter.Rates(), "Rates should return constant rates") + assert.Equal(t, initialFakeTime, currencyConverter.LastUpdated(), "LastUpdated return is incorrect") - time.Sleep(400 * time.Millisecond) - // Rates have been invalid for ~400ms - assert.Equal(t, currencyConverter.Rates(), ¤cies.ConstantRates{}, "Rates() should return constant rates") + // Fifth Update call is successful and yields valid rates + err5 := currencyConverter.Run() + assert.Nil(t, err5) - time.Sleep(200 * time.Millisecond) - // Rates have been valid for ~100ms - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + // Verify rates are valid and last update ts has changed + thirtyOneSec := 31 * time.Second + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") + assert.Equal(t, (initialFakeTime.Add(thirtyOneSec)), currencyConverter.LastUpdated(), "LastUpdated should be set") } -func TestRatesAreNeverStale(t *testing.T) { - callCnt := 0 +func TestRatesAreNeverConsideredStale(t *testing.T) { + callCount := 0 mockedHttpServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { - if callCnt == 0 { + callCount++ + if callCount == 1 { rw.WriteHeader(http.StatusOK) rw.Write([]byte(getMockRates())) } else { rw.WriteHeader(http.StatusNotFound) + rw.Write([]byte(`{"message": "Not Found"}`)) } - callCnt++ }), ) defer mockedHttpServer.Close() - expectedRates := ¤cies.Rates{ + expectedRates := &Rates{ DataAsOf: time.Date(2018, time.September, 12, 0, 0, 0, 0, time.UTC), Conversions: map[string]map[string]float64{ "USD": { @@ -557,25 +268,38 @@ func TestRatesAreNeverStale(t *testing.T) { }, } + initialFakeTime := time.Date(2018, time.September, 12, 30, 0, 0, 0, time.UTC) + fakeTime := &FakeTime{time: initialFakeTime} + // Execute: - currencyConverter := currencies.NewRateConverter( + currencyConverter := NewRateConverter( &http.Client{}, mockedHttpServer.URL, - time.Duration(100)*time.Millisecond, - time.Duration(0)*time.Millisecond, + 0*time.Millisecond, // stale rates threshold ) + currencyConverter.time = fakeTime + + // First Update call is successful and yields valid rates + err1 := currencyConverter.Run() + assert.Nil(t, err1) + + // Verify rates are valid and last update timestamp is correct + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") + assert.Equal(t, fakeTime.time, currencyConverter.LastUpdated(), "LastUpdated should be set") + + // Advance time so the current time is well past the the time the rates were last updated + fakeTime.time = initialFakeTime.Add(24 * time.Hour) - // Verify: - // Rates are valid at t=0 and are then invalid at 100ms - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + // Second Update call results in error but rates from a day ago are still valid + err2 := currencyConverter.Run() + assert.NotNil(t, err2) - time.Sleep(500 * time.Millisecond) - // Rates have been invalid for ~400ms - assert.Equal(t, currencyConverter.Rates(), expectedRates, "Rates() should return expected rates") + // Verify rates are valid and last update ts is correct + assert.Equal(t, expectedRates, currencyConverter.Rates(), "Conversions.Rates weren't the expected ones") + assert.Equal(t, initialFakeTime, currencyConverter.LastUpdated(), "LastUpdated should be set") } func TestRace(t *testing.T) { - // This test is checking that no race conditions appear in rate converter. // It simulate multiple clients (in different goroutines) asking for updates // and rates while the rate converter is also updating periodically. @@ -599,20 +323,19 @@ func TestRace(t *testing.T) { } // Execute: - - // Create a rate converter which will be fetching new values every 10 ms - currencyConverter := currencies.NewRateConverter( + // Create a rate converter which will be fetching new values every 1 ms + interval := 1 * time.Millisecond + currencyConverter := NewRateConverter( mockedHttpClient, "currency.fake.com", - time.Duration(10)*time.Millisecond, - time.Duration(24)*time.Hour, + 24*time.Hour, ) - defer currencyConverter.StopPeriodicFetching() + ticker := task.NewTickerTask(interval, currencyConverter) + ticker.Start() + defer ticker.Stop() - // Create 50 clients asking for updates and rates conversion at random intervals - // from 1ms to 50ms for 10 seconds var wg sync.WaitGroup - clientsCount := 50 + clientsCount := 10 wg.Add(clientsCount) dones := make([]chan bool, clientsCount) @@ -623,12 +346,9 @@ func TestRace(t *testing.T) { clientTicker := time.NewTicker(randomTickInterval) for { select { - case tickTime := <-clientTicker.C: - // Either ask for an Update() or for GetRate() - // based on the tick ms - tickMs := tickTime.UnixNano() / int64(time.Millisecond) - if tickMs%2 == 0 { - err := currencyConverter.Update() + case <-clientTicker.C: + if clientNum < 5 { + err := currencyConverter.Run() assert.Nil(t, err) } else { rate, err := currencyConverter.Rates().GetRate("USD", "GBP") @@ -643,7 +363,7 @@ func TestRace(t *testing.T) { }(dones[c], c) } - time.Sleep(10 * time.Second) + time.Sleep(100 * time.Millisecond) // Sending stop signals to all clients for i := range dones { dones[i] <- true diff --git a/currencies/rates_test.go b/currencies/rates_test.go index 915b817d7a5..5b1c4497b63 100644 --- a/currencies/rates_test.go +++ b/currencies/rates_test.go @@ -146,6 +146,7 @@ func TestGetRate(t *testing.T) { {from: "", to: "EUR", expectedRate: 0, hasError: true}, {from: "CNY", to: "", expectedRate: 0, hasError: true}, {from: "", to: "", expectedRate: 0, hasError: true}, + {from: "USD", to: "USD", expectedRate: 1, hasError: false}, } for _, tc := range testCases { diff --git a/endpoints/currency_rates.go b/endpoints/currency_rates.go index 745dbe3e7d4..90650cc2886 100644 --- a/endpoints/currency_rates.go +++ b/endpoints/currency_rates.go @@ -24,7 +24,7 @@ type rateConverter interface { } // newCurrencyRatesInfo creates a new CurrencyRatesInfo instance. -func newCurrencyRatesInfo(rateConverter rateConverter) currencyRatesInfo { +func newCurrencyRatesInfo(rateConverter rateConverter, fetchingInterval time.Duration) currencyRatesInfo { currencyRatesInfo := currencyRatesInfo{ Active: false, @@ -44,7 +44,6 @@ func newCurrencyRatesInfo(rateConverter rateConverter) currencyRatesInfo { source := infos.Source() currencyRatesInfo.Source = &source - fetchingInterval := infos.FetchingInterval() currencyRatesInfo.FetchingInterval = &fetchingInterval lastUpdated := infos.LastUpdated() @@ -57,8 +56,8 @@ func newCurrencyRatesInfo(rateConverter rateConverter) currencyRatesInfo { } // NewCurrencyRatesEndpoint returns current currency rates applied by the PBS server. -func NewCurrencyRatesEndpoint(rateConverter rateConverter) http.HandlerFunc { - currencyRateInfo := newCurrencyRatesInfo(rateConverter) +func NewCurrencyRatesEndpoint(rateConverter rateConverter, fetchingInterval time.Duration) http.HandlerFunc { + currencyRateInfo := newCurrencyRatesInfo(rateConverter, fetchingInterval) return func(w http.ResponseWriter, _ *http.Request) { jsonOutput, err := json.Marshal(currencyRateInfo) diff --git a/endpoints/currency_rates_test.go b/endpoints/currency_rates_test.go index e0b127fcd95..86c4e50fb3e 100644 --- a/endpoints/currency_rates_test.go +++ b/endpoints/currency_rates_test.go @@ -14,20 +14,21 @@ import ( func TestCurrencyRatesEndpoint(t *testing.T) { // Setup: var testCases = []struct { - input rateConverter - expectedBody string - expectedCode int - description string + inputConverter rateConverter + inputFetchingInterval time.Duration + expectedBody string + expectedCode int + description string }{ { nil, + time.Duration(0), `{"active": false}`, http.StatusOK, "case 1 - rate converter is nil", }, { newRateConverterMock( - 5*time.Minute, "https://sync.test.com", time.Date(2019, 3, 2, 12, 54, 56, 651387237, time.UTC), newConversionMock(&map[string]map[string]float64{ @@ -36,6 +37,7 @@ func TestCurrencyRatesEndpoint(t *testing.T) { }, }), ), + 5 * time.Minute, `{ "active": true, "source": "https://sync.test.com", @@ -52,11 +54,11 @@ func TestCurrencyRatesEndpoint(t *testing.T) { }, { newRateConverterMock( - time.Duration(0), "", time.Time{}, nil, ), + time.Duration(0), `{ "active": true, "source": "", @@ -70,12 +72,14 @@ func TestCurrencyRatesEndpoint(t *testing.T) { newRateConverterMockWithInfo( newUnmarshableConverterInfoMock(), ), + time.Duration(0), "", http.StatusInternalServerError, "case 4 - invalid rates input for marshaling", }, { newRateConverterMockWithNilInfo(), + time.Duration(0), `{ "active": true }`, @@ -86,7 +90,7 @@ func TestCurrencyRatesEndpoint(t *testing.T) { for _, tc := range testCases { - handler := NewCurrencyRatesEndpoint(tc.input) + handler := NewCurrencyRatesEndpoint(tc.inputConverter, tc.inputFetchingInterval) w := httptest.NewRecorder() // Execute: @@ -117,21 +121,16 @@ func newConversionMock(rates *map[string]map[string]float64) *conversionMock { } type converterInfoMock struct { - source string - fetchingInterval time.Duration - lastUpdated time.Time - rates *map[string]map[string]float64 - additionalInfo interface{} + source string + lastUpdated time.Time + rates *map[string]map[string]float64 + additionalInfo interface{} } func (m converterInfoMock) Source() string { return m.source } -func (m converterInfoMock) FetchingInterval() time.Duration { - return m.fetchingInterval -} - func (m converterInfoMock) LastUpdated() time.Time { return m.lastUpdated } @@ -150,10 +149,6 @@ func (m unmarshableConverterInfoMock) Source() string { return "" } -func (m unmarshableConverterInfoMock) FetchingInterval() time.Duration { - return time.Duration(0) -} - func (m unmarshableConverterInfoMock) LastUpdated() time.Time { return time.Time{} } @@ -172,7 +167,6 @@ func newUnmarshableConverterInfoMock() unmarshableConverterInfoMock { } type rateConverterMock struct { - fetchingInterval time.Duration syncSourceURL string rates *conversionMock lastUpdated time.Time @@ -197,23 +191,20 @@ func (m rateConverterMock) GetInfo() currencies.ConverterInfo { rates = m.rates.GetRates() } return converterInfoMock{ - source: m.syncSourceURL, - fetchingInterval: m.fetchingInterval, - lastUpdated: m.lastUpdated, - rates: rates, + source: m.syncSourceURL, + lastUpdated: m.lastUpdated, + rates: rates, } } func newRateConverterMock( - fetchingInterval time.Duration, syncSourceURL string, lastUpdated time.Time, rates *conversionMock) rateConverterMock { return rateConverterMock{ - fetchingInterval: fetchingInterval, - syncSourceURL: syncSourceURL, - rates: rates, - lastUpdated: lastUpdated, + syncSourceURL: syncSourceURL, + rates: rates, + lastUpdated: lastUpdated, } } diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go index 93d7575e865..fba0daecea8 100644 --- a/endpoints/openrtb2/auction_benchmark_test.go +++ b/endpoints/openrtb2/auction_benchmark_test.go @@ -5,6 +5,7 @@ import ( "net/http/httptest" "strings" "testing" + "time" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/currencies" @@ -77,7 +78,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) { theMetrics, infos, gdpr.AlwaysAllow{}, - currencies.NewRateConverterDefault(), + currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)), ), paramValidator, empty_fetcher.EmptyFetcher{}, diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 7ae96c09b93..4f207cf5a65 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -75,7 +75,7 @@ func TestSingleBidder(t *testing.T) { bidResponse: mockBidderResponse, } bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) // Make sure the goodSingleBidder was called with the expected arguments. @@ -163,7 +163,7 @@ func TestMultiBidder(t *testing.T) { bidResponse: mockBidderResponse, } bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if seatBid == nil { @@ -528,9 +528,11 @@ func TestMultiCurrencies(t *testing.T) { currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, - time.Duration(10)*time.Second, time.Duration(24)*time.Hour, ) + time.Sleep(time.Duration(500) * time.Millisecond) + currencyConverter.Run() + seatBid, errs := bidder.requestBid( context.Background(), &openrtb.BidRequest{}, @@ -674,7 +676,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) { // Execute: bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBid, errs := bidder.requestBid( context.Background(), &openrtb.BidRequest{}, @@ -843,7 +845,6 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { currencyConverter := currencies.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, - time.Duration(10)*time.Second, time.Duration(24)*time.Hour, ) seatBid, errs := bidder.requestBid( @@ -1020,7 +1021,7 @@ func TestMobileNativeTypes(t *testing.T) { bidResponse: tc.mockBidderResponse, } bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBids, _ := bidder.requestBid( context.Background(), @@ -1041,7 +1042,7 @@ func TestMobileNativeTypes(t *testing.T) { func TestErrorReporting(t *testing.T) { bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if bids != nil { t.Errorf("There should be no seatbid if no http requests are returned.") @@ -1224,7 +1225,8 @@ func TestCallRecordAdapterConnections(t *testing.T) { // Run requestBid using an http.Client with a mock handler bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus) - _, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencies.NewRateConverterDefault().Rates(), &adapters.ExtraRequestInfo{}) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + _, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) // Assert no errors assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 5fbdb1c57a9..545f04fd0ef 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -50,7 +50,8 @@ func TestNewExchange(t *testing.T) { Adapters: blankAdapterConfig(openrtb_ext.BidderList()), } - e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), knownAdapters, config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), knownAdapters, config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) for _, bidderName := range knownAdapters { if _, ok := e.adapterMap[bidderName]; !ok { t.Errorf("NewExchange produced an Exchange without bidder %s", bidderName) @@ -87,7 +88,8 @@ func TestCharacterEscape(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer)) defer server.Close() - e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) /* 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs */ //liveAdapters []openrtb_ext.BidderName, @@ -230,7 +232,7 @@ func TestDebugBehaviour(t *testing.T) { e.cache = &wellBehavedCache{} e.me = &metricsConf.DummyMetricsEngine{} e.gDPR = gdpr.AlwaysAllow{} - e.currencyConverter = currencies.NewRateConverterDefault() + e.currencyConverter = currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) // Run tests for _, test := range testCases { @@ -299,7 +301,8 @@ func TestGetBidCacheInfo(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer)) defer server.Close() - e := NewExchange(server.Client(), pbc.NewClient(&http.Client{}, &cfg.CacheURL, &cfg.ExtCacheURL, testEngine), cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(server.Client(), pbc.NewClient(&http.Client{}, &cfg.CacheURL, &cfg.ExtCacheURL, testEngine), cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) /* 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs */ liveAdapters := []openrtb_ext.BidderName{bidderName} @@ -449,7 +452,8 @@ func TestBidResponseCurrency(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer)) defer server.Close() - e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) liveAdapters := make([]openrtb_ext.BidderName, 1) liveAdapters[0] = "appnexus" @@ -616,7 +620,8 @@ func TestRaceIntegration(t *testing.T) { t.Errorf("Failed to create a category Fetcher: %v", error) } theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - ex := NewExchange(server.Client(), &wellBehavedCache{}, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + ex := NewExchange(server.Client(), &wellBehavedCache{}, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter) _, err := ex.HoldAuction(context.Background(), newRaceCheckingRequest(t), &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) @@ -700,7 +705,8 @@ func TestPanicRecovery(t *testing.T) { } theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - e := NewExchange(&http.Client{}, nil, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(&http.Client{}, nil, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) chBids := make(chan *bidResponseWrapper, 1) panicker := func(aName openrtb_ext.BidderName, coreBidder openrtb_ext.BidderName, request *openrtb.BidRequest, bidlabels *pbsmetrics.AdapterLabels, conversions currencies.Conversions) { panic("panic!") @@ -765,7 +771,8 @@ func TestPanicRecoveryHighLevel(t *testing.T) { Endpoint: server.URL, } } - e := NewExchange(server.Client(), &mockCache{}, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e := NewExchange(server.Client(), &mockCache{}, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter).(*exchange) e.adapterMap[openrtb_ext.BidderBeachfront] = panicingAdapter{} e.adapterMap[openrtb_ext.BidderAppnexus] = panicingAdapter{} @@ -1025,7 +1032,7 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] cache: &wellBehavedCache{}, cacheTime: 0, gDPR: gdpr.AlwaysAllow{}, - currencyConverter: currencies.NewRateConverterDefault(), + currencyConverter: currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)), UsersyncIfAmbiguous: false, privacyConfig: privacyConfig, } diff --git a/exchange/legacy_test.go b/exchange/legacy_test.go index 3ca804a115c..61414c0ed73 100644 --- a/exchange/legacy_test.go +++ b/exchange/legacy_test.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "errors" + "net/http" "reflect" "testing" + "time" "github.com/buger/jsonparser" "github.com/evanphx/json-patch" @@ -58,7 +60,7 @@ func TestSiteVideo(t *testing.T) { mockAdapter := mockLegacyAdapter{} exchangeBidder := adaptLegacyAdapter(&mockAdapter) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if len(errs) > 0 { t.Errorf("Unexpected error requesting bids: %v", errs) @@ -92,7 +94,7 @@ func TestAppBanner(t *testing.T) { mockAdapter := mockLegacyAdapter{} exchangeBidder := adaptLegacyAdapter(&mockAdapter) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if len(errs) > 0 { t.Errorf("Unexpected error requesting bids: %v", errs) @@ -138,7 +140,7 @@ func TestBidTransforms(t *testing.T) { } exchangeBidder := adaptLegacyAdapter(&mockAdapter) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBid, errs := exchangeBidder.requestBid(context.Background(), newAppOrtbRequest(), openrtb_ext.BidderRubicon, bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if len(errs) != 1 { t.Fatalf("Bad error count. Expected 1, got %d", len(errs)) @@ -287,7 +289,7 @@ func TestErrorResponse(t *testing.T) { } exchangeBidder := adaptLegacyAdapter(&mockAdapter) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if len(errs) != 1 { t.Fatalf("Bad error count. Expected 1, got %d", len(errs)) @@ -326,7 +328,7 @@ func TestWithTargeting(t *testing.T) { }}, } exchangeBidder := adaptLegacyAdapter(&mockAdapter) - currencyConverter := currencies.NewRateConverterDefault() + currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) bid, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderFacebook, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) if len(errs) != 0 { t.Fatalf("This should not produce errors. Got %v", errs) diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 284d56be42e..e596e5aa215 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -88,7 +88,7 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op cache: &wellBehavedCache{}, cacheTime: time.Duration(0), gDPR: gdpr.AlwaysAllow{}, - currencyConverter: currencies.NewRateConverterDefault(), + currencyConverter: currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)), UsersyncIfAmbiguous: false, } diff --git a/main.go b/main.go index 9a835f42a4c..035d386e3b0 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( pbc "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/router" "github.com/prebid/prebid-server/server" + "github.com/prebid/prebid-server/util/task" "github.com/golang/glog" "github.com/spf13/viper" @@ -53,8 +54,10 @@ func loadConfig() (*config.Configuration, error) { func serve(revision string, cfg *config.Configuration) error { fetchingInterval := time.Duration(cfg.CurrencyConverter.FetchIntervalSeconds) * time.Second staleRatesThreshold := time.Duration(cfg.CurrencyConverter.StaleRatesSeconds) * time.Second - currencyConverter := currencies.NewRateConverter(&http.Client{}, cfg.CurrencyConverter.FetchURL, - fetchingInterval, staleRatesThreshold) + currencyConverter := currencies.NewRateConverter(&http.Client{}, cfg.CurrencyConverter.FetchURL, staleRatesThreshold) + + currencyConverterTickerTask := task.NewTickerTask(fetchingInterval, currencyConverter) + currencyConverterTickerTask.Start() r, err := router.New(cfg, currencyConverter) if err != nil { @@ -64,7 +67,7 @@ func serve(revision string, cfg *config.Configuration) error { pbc.InitPrebidCache(cfg.CacheURL.GetBaseURL()) corsRouter := router.SupportCORS(r) - server.Listen(cfg, router.NoCache{Handler: corsRouter}, router.Admin(revision, currencyConverter), r.MetricsEngine) + server.Listen(cfg, router.NoCache{Handler: corsRouter}, router.Admin(revision, currencyConverter, fetchingInterval), r.MetricsEngine) r.Shutdown() return nil diff --git a/router/admin.go b/router/admin.go index 83c4701bb19..fe268c48b2c 100644 --- a/router/admin.go +++ b/router/admin.go @@ -3,12 +3,13 @@ package router import ( "net/http" "net/http/pprof" + "time" "github.com/prebid/prebid-server/currencies" "github.com/prebid/prebid-server/endpoints" ) -func Admin(revision string, rateConverter *currencies.RateConverter) *http.ServeMux { +func Admin(revision string, rateConverter *currencies.RateConverter, rateConverterFetchingInterval time.Duration) *http.ServeMux { // Add endpoints to the admin server // Making sure to add pprof routes mux := http.NewServeMux() @@ -19,7 +20,7 @@ func Admin(revision string, rateConverter *currencies.RateConverter) *http.Serve mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) mux.HandleFunc("/debug/pprof/trace", pprof.Trace) // Register prebid-server defined admin handlers - mux.HandleFunc("/currency/rates", endpoints.NewCurrencyRatesEndpoint(rateConverter)) + mux.HandleFunc("/currency/rates", endpoints.NewCurrencyRatesEndpoint(rateConverter, rateConverterFetchingInterval)) mux.HandleFunc("/version", endpoints.NewVersionEndpoint(revision)) return mux } diff --git a/util/task/ticker_task.go b/util/task/ticker_task.go new file mode 100644 index 00000000000..a8d523b75d5 --- /dev/null +++ b/util/task/ticker_task.go @@ -0,0 +1,53 @@ +package task + +import ( + "time" +) + +type Runner interface { + Run() error +} + +type TickerTask struct { + interval time.Duration + runner Runner + done chan struct{} +} + +func NewTickerTask(interval time.Duration, runner Runner) *TickerTask { + return &TickerTask{ + interval: interval, + runner: runner, + done: make(chan struct{}), + } +} + +// Start runs the task immediately and then schedules the task to run periodically +// if a positive fetching interval has been specified. +func (t *TickerTask) Start() { + t.runner.Run() + + if t.interval > 0 { + go t.runRecurring() + } +} + +// Stop stops the periodic task but the task runner maintains state +func (t *TickerTask) Stop() { + close(t.done) +} + +// run creates a ticker that ticks at the specified interval. On each tick, +// the task is executed +func (t *TickerTask) runRecurring() { + ticker := time.NewTicker(t.interval) + + for { + select { + case <-ticker.C: + t.runner.Run() + case <-t.done: + return + } + } +} diff --git a/util/task/ticker_task_test.go b/util/task/ticker_task_test.go new file mode 100644 index 00000000000..27551c9a2c2 --- /dev/null +++ b/util/task/ticker_task_test.go @@ -0,0 +1,63 @@ +package task_test + +import ( + "testing" + "time" + + "github.com/prebid/prebid-server/util/task" + "github.com/stretchr/testify/assert" +) + +type MockRunner struct { + RunCount int +} + +func (mcc *MockRunner) Run() error { + mcc.RunCount++ + return nil +} + +func TestStartWithSingleRun(t *testing.T) { + // Setup: + runner := &MockRunner{RunCount: 0} + interval := 0 * time.Millisecond + ticker := task.NewTickerTask(interval, runner) + + // Execute: + ticker.Start() + time.Sleep(10 * time.Millisecond) + + // Verify: + assert.Equal(t, runner.RunCount, 1, "runner should have run one time") +} + +func TestStartWithPeriodicRun(t *testing.T) { + // Setup: + runner := &MockRunner{RunCount: 0} + interval := 10 * time.Millisecond + ticker := task.NewTickerTask(interval, runner) + + // Execute: + ticker.Start() + time.Sleep(25 * time.Millisecond) + ticker.Stop() + + // Verify: + assert.Equal(t, runner.RunCount, 3, "runner should have run three times") +} + +func TestStop(t *testing.T) { + // Setup: + runner := &MockRunner{RunCount: 0} + interval := 10 * time.Millisecond + ticker := task.NewTickerTask(interval, runner) + + // Execute: + ticker.Start() + time.Sleep(25 * time.Millisecond) + ticker.Stop() + time.Sleep(25 * time.Millisecond) // wait in case stop failed so additional runs can happen + + // Verify: + assert.Equal(t, runner.RunCount, 3, "runner should have run three times") +} diff --git a/util/timeutil/time.go b/util/timeutil/time.go new file mode 100644 index 00000000000..e8eaae7d61f --- /dev/null +++ b/util/timeutil/time.go @@ -0,0 +1,16 @@ +package timeutil + +import ( + "time" +) + +type Time interface { + Now() time.Time +} + +// RealTime wraps the time package for testability +type RealTime struct{} + +func (c *RealTime) Now() time.Time { + return time.Now() +} From a4ac6b63f312ac94d4844b7129fcf8f3dd044204 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Wed, 12 Aug 2020 18:57:54 -0400 Subject: [PATCH 166/318] Fix TCF1 Fetcher Fallback (#1438) --- gdpr/vendorlist-fetching.go | 2 +- gdpr/vendorlist-fetching_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index a0a73c93008..1442f81c3ba 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -158,7 +158,7 @@ func newVendorListCache(fallbackVL api.VendorList) (save func(id uint16, list ap if ok { return list.(vendorlist.VendorList) } - return fallbackVL + return nil } return } diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index c989ef4cef8..031e564094c 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -173,7 +173,7 @@ func TestDefaultVendorList(t *testing.T) { assert.Equal(t, false, vendor.Purpose(2)) } -func TestDefaultVendorListPassthrough(t *testing.T) { +func TestFallbackVendorListPassthrough(t *testing.T) { firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ 32: { purposes: []int{1, 2}, @@ -184,7 +184,7 @@ func TestDefaultVendorListPassthrough(t *testing.T) { purposes: []int{2}, }, }) - server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ + server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ 1: firstVendorList, 2: secondVendorList, }))) @@ -204,7 +204,7 @@ func TestDefaultVendorListPassthrough(t *testing.T) { assert.Equal(t, true, vendor.Purpose(2)) } -func TestDefaultVendorListNoFetch(t *testing.T) { +func TestFallbackVendorListNoFetch(t *testing.T) { firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ 32: { purposes: []int{1, 2}, From 5a7d3652d448e179be7add376047b562c115d0ef Mon Sep 17 00:00:00 2001 From: chino117 Date: Mon, 17 Aug 2020 11:09:22 -0300 Subject: [PATCH 167/318] Eplanning adapter: Get domain from page (#1434) --- adapters/eplanning/eplanning.go | 25 ++++--- .../supplemental/bad-page-site.json | 31 ++++++++ .../site-page-and-url-correctly-parsed.json | 75 +++++++++++++++++++ 3 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 adapters/eplanning/eplanningtest/supplemental/bad-page-site.json create mode 100644 adapters/eplanning/eplanningtest/supplemental/site-page-and-url-correctly-parsed.json diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go index 2a46b5469e0..032edfd1b06 100644 --- a/adapters/eplanning/eplanning.go +++ b/adapters/eplanning/eplanning.go @@ -104,25 +104,28 @@ func (adapter *EPlanningAdapter) MakeRequests(request *openrtb.BidRequest, reqIn } } - var pageURL string + pageURL := defaultPageURL if request.Site != nil && request.Site.Page != "" { pageURL = request.Site.Page - } else { - pageURL = defaultPageURL } - var pageDomain string - if request.Site != nil && request.Site.Domain != "" { - pageDomain = request.Site.Domain - } else { - pageDomain = defaultPageURL + pageDomain := defaultPageURL + if request.Site != nil { + if request.Site.Domain != "" { + pageDomain = request.Site.Domain + } else if request.Site.Page != "" { + u, err := url.Parse(request.Site.Page) + if err != nil { + errors = append(errors, err) + return nil, errors + } + pageDomain = u.Hostname() + } } - var requestTarget string + requestTarget := pageDomain if request.App != nil && request.App.Bundle != "" { requestTarget = request.App.Bundle - } else { - requestTarget = pageDomain } uriObj, err := url.Parse(adapter.URI) diff --git a/adapters/eplanning/eplanningtest/supplemental/bad-page-site.json b/adapters/eplanning/eplanningtest/supplemental/bad-page-site.json new file mode 100644 index 00000000000..5efe604f7e6 --- /dev/null +++ b/adapters/eplanning/eplanningtest/supplemental/bad-page-site.json @@ -0,0 +1,31 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "bidder": { + "ci": "12345", + "adunit_code": "test_adunitcode" + } + } + } + ], + "site": { + "page": "http://www.page%test.com" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "parse (\\\")?http://www.page%test.com(\\\")?: invalid URL escape \\\"%te\\\"", + "comparison": "regex" + } + ] +} + diff --git a/adapters/eplanning/eplanningtest/supplemental/site-page-and-url-correctly-parsed.json b/adapters/eplanning/eplanningtest/supplemental/site-page-and-url-correctly-parsed.json new file mode 100644 index 00000000000..20a419cdbfd --- /dev/null +++ b/adapters/eplanning/eplanningtest/supplemental/site-page-and-url-correctly-parsed.json @@ -0,0 +1,75 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 600, + "h": 300 + }, + "ext": { + "bidder": { + "ci": "12345", + "adunit_code": "test_adunitcode" + } + } + } + ], + "site": { + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://rtb.e-planning.net/pbs/1/12345/1/www.publisher.com/ROS?e=testadunitcode%3A600x300&ncb=1&ur=http%3A%2F%2Fwww.publisher.com%2Fawesome%2Fsite%3Fwith%3Dsome%26parameters%3Dhere", + "body": {} + }, + "mockResponse": { + "status": 200, + "body": { + "sI": { "k": "12345" }, + "sec": "ROS", + "sp": [ + { + "k": "testadunitcode", + "a": [{ + "i": "123456789abcdef", + "pr": "0.5", + "adm": "
test
", + "crid": "abcdef123456789", + "id": "adid12345", + "w": 600, + "h": 300 + }] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "123456789abcdef", + "impid": "test-imp-id", + "price": 0.5, + "adm": "
test
", + "adid": "adid12345", + "crid": "abcdef123456789", + "w": 600, + "h": 300 + }, + "type": "banner" + } + ] + } + ] + } + From e065488276139a56adf9500908bb93fc70c1ac91 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Mon, 17 Aug 2020 08:17:15 -0700 Subject: [PATCH 168/318] Fix no bid debug log (#1375) --- endpoints/openrtb2/video_auction.go | 45 +++++------- endpoints/openrtb2/video_auction_test.go | 71 ++++++++++++++++++ exchange/auction.go | 51 ++++++++++++- .../debuglog_enabled_no_winners_nor_bids.json | 54 ++++++++++++++ exchange/exchange.go | 18 +++++ .../debuglog_enabled_no_bids.json | 72 +++++++++++++++++++ openrtb_ext/bid_response_video.go | 6 +- 7 files changed, 283 insertions(+), 34 deletions(-) create mode 100644 exchange/cachetest/debuglog_enabled_no_winners_nor_bids.json create mode 100644 exchange/exchangetest/debuglog_enabled_no_bids.json diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 49ba287610b..a6ca527874a 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -122,7 +122,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re defer func() { if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil { - err := putDebugLogError(deps.cache, &debugLog, start) + err := debugLog.PutDebugLogError(deps.cache, deps.cfg.CacheURL.ExpectedTimeMillis, vo.Errors) if err != nil { vo.Errors = append(vo.Errors, err) } @@ -279,6 +279,21 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re bidResp.Ext = response.Ext } + if len(bidResp.AdPods) == 0 && debugLog.Enabled { + err := debugLog.PutDebugLogError(deps.cache, deps.cfg.CacheURL.ExpectedTimeMillis, vo.Errors) + if err != nil { + vo.Errors = append(vo.Errors, err) + } else { + bidResp.AdPods = append(bidResp.AdPods, &openrtb_ext.AdPod{ + Targeting: []openrtb_ext.VideoTargeting{ + { + HbCacheID: debugLog.CacheKey, + }, + }, + }) + } + } + vo.VideoResponse = bidResp resp, err := json.Marshal(bidResp) @@ -294,34 +309,6 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } -func putDebugLogError(cache prebid_cache_client.Client, debugLog *exchange.DebugLog, start time.Time) error { - debugLog.Data.Response = "No response created" - - debugLog.BuildCacheString() - - data, err := json.Marshal(debugLog.CacheString) - if err != nil { - return err - } - - toCache := []prebid_cache_client.Cacheable{ - { - Type: debugLog.CacheType, - Data: data, - TTLSeconds: debugLog.TTL, - Key: "log_" + debugLog.CacheKey, - }, - } - - if cache != nil { - ctx, cancel := context.WithDeadline(context.Background(), start.Add(time.Duration(100)*time.Millisecond)) - defer cancel() - cache.PutJson(ctx, toCache) - } - - return nil -} - func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []PodError) *openrtb_ext.BidRequestVideo { for i := len(podErrors) - 1; i >= 0; i-- { videoReq.PodConfig.Pods = append(videoReq.PodConfig.Pods[:podErrors[i].PodIndex], videoReq.PodConfig.Pods[podErrors[i].PodIndex+1:]...) diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index b15c6a7b47a..534db3c79e2 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -284,6 +284,42 @@ func TestVideoEndpointDebugError(t *testing.T) { assert.Equal(t, recorder.Code, 500, "Should catch error in request") } +func TestVideoEndpointDebugNoAdPods(t *testing.T) { + ex := &mockExchangeVideoNoBids{ + cache: &mockCacheClient{}, + } + reqData, err := ioutil.ReadFile("sample-requests/video/video_valid_sample.json") + if err != nil { + t.Fatalf("Failed to fetch a valid request: %v", err) + } + reqBody := string(getRequestPayload(t, reqData)) + req := httptest.NewRequest("POST", "/openrtb2/video?debug=true", strings.NewReader(reqBody)) + recorder := httptest.NewRecorder() + + deps := mockDepsNoBids(t, ex) + deps.VideoAuctionEndpoint(recorder, req, nil) + + if ex.lastRequest == nil { + t.Fatalf("The request never made it into the Exchange.") + } + if !ex.cache.called { + t.Fatalf("Cache was not called when it should have been") + } + + respBytes := recorder.Body.Bytes() + resp := &openrtb_ext.BidResponseVideo{} + if err := json.Unmarshal(respBytes, resp); err != nil { + t.Fatalf("Unable to unmarshal response.") + } + + assert.Len(t, resp.AdPods, 1, "Debug AdPod should be added to response") + assert.Empty(t, resp.AdPods[0].Errors, "AdPod Errors should be empty") + assert.Empty(t, resp.AdPods[0].Targeting[0].HbPb, "Hb_pb should be empty") + assert.Empty(t, resp.AdPods[0].Targeting[0].HbPbCatDur, "Hb_pb_cat_dur should be empty") + assert.NotEmpty(t, resp.AdPods[0].Targeting[0].HbCacheID, "Hb_cache_id should not be empty") + assert.Equal(t, int64(0), resp.AdPods[0].PodId, "Pod ID should be 0") +} + func TestVideoEndpointNoPods(t *testing.T) { ex := &mockExchangeVideo{} reqData, err := ioutil.ReadFile("sample-requests/video/video_invalid_sample.json") @@ -1189,6 +1225,29 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { return deps } +func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps { + theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + edep := &endpointDeps{ + ex, + newParamsValidator(t), + &mockVideoStoredReqFetcher{}, + &mockVideoStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{MaxRequestSize: maxSize}, + theMetrics, + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BidderMap, + ex.cache, + regexp.MustCompile(`[<>]`), + hardcodedResponseIPValidator{response: true}, + } + + return edep +} + type mockCacheClient struct { called bool } @@ -1247,6 +1306,18 @@ func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb }, nil } +type mockExchangeVideoNoBids struct { + lastRequest *openrtb.BidRequest + cache *mockCacheClient +} + +func (m *mockExchangeVideoNoBids) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { + m.lastRequest = bidRequest + return &openrtb.BidResponse{ + SeatBid: []openrtb.SeatBid{{}}, + }, nil +} + var testVideoStoredImpData = map[string]json.RawMessage{ "fba10607-0c12-43d1-ad07-b8a513bc75d6": json.RawMessage(`{"ext": {"appnexus": {"placementId": 14997137}}}`), "8b452b41-2681-4a20-9086-6f16ffad7773": json.RawMessage(`{"ext": {"appnexus": {"placementId": 15016213}}}`), diff --git a/exchange/auction.go b/exchange/auction.go index 45e1422540e..aa446ddba13 100644 --- a/exchange/auction.go +++ b/exchange/auction.go @@ -8,6 +8,7 @@ import ( "fmt" "regexp" "strings" + "time" uuid "github.com/gofrs/uuid" "github.com/golang/glog" @@ -47,6 +48,52 @@ func (d *DebugLog) BuildCacheString() { d.CacheString = fmt.Sprintf("%s%s%s%s", xml.Header, d.Data.Request, d.Data.Headers, d.Data.Response) } +func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout int, errors []error) error { + if len(d.Data.Response) == 0 && len(errors) == 0 { + d.Data.Response = "No response or errors created" + } + + if len(errors) > 0 { + errStrings := []string{} + for _, err := range errors { + errStrings = append(errStrings, err.Error()) + } + d.Data.Response = fmt.Sprintf("%s\nErrors:\n%s", d.Data.Response, strings.Join(errStrings, "\n")) + } + + d.BuildCacheString() + + if len(d.CacheKey) == 0 { + rawUUID, err := uuid.NewV4() + if err != nil { + return err + } + d.CacheKey = rawUUID.String() + } + + data, err := json.Marshal(d.CacheString) + if err != nil { + return err + } + + toCache := []prebid_cache_client.Cacheable{ + { + Type: d.CacheType, + Data: data, + TTLSeconds: d.TTL, + Key: "log_" + d.CacheKey, + }, + } + + if cache != nil { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Duration(timeout)*time.Millisecond)) + defer cancel() + cache.PutJson(ctx, toCache) + } + + return nil +} + func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int) *auction { winningBids := make(map[string]*pbsOrtbBid, numImps) winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName]*pbsOrtbBid, numImps) @@ -179,9 +226,9 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, } } - if debugLog != nil && debugLog.Enabled { - debugLog.BuildCacheString() + if len(toCache) > 0 && debugLog != nil && debugLog.Enabled { debugLog.CacheKey = hbCacheID + debugLog.BuildCacheString() if jsonBytes, err := json.Marshal(debugLog.CacheString); err == nil { toCache = append(toCache, prebid_cache_client.Cacheable{ Type: debugLog.CacheType, diff --git a/exchange/cachetest/debuglog_enabled_no_winners_nor_bids.json b/exchange/cachetest/debuglog_enabled_no_winners_nor_bids.json new file mode 100644 index 00000000000..637b33e171b --- /dev/null +++ b/exchange/cachetest/debuglog_enabled_no_winners_nor_bids.json @@ -0,0 +1,54 @@ +{ + "debugLog": { + "Enabled": true, + "CacheType": "xml", + "TTL": 3600, + "Data": { + "Request": "test request string", + "Headers": "test headers string", + "Response": "test response string" + } + }, + "bidRequest": { + "imp": [ + { + "id": "oneImp", + "exp": 600 + }, + { + "id": "twoImp" + } + ] + }, + "pbsBids": [ + { + "bid": { + "id": "bidOne", + "impid": "oneImp", + "price": 7.64 + }, + "bidType": "video", + "bidder": "appnexus" + }, + { + "bid": { + "id": "bidTwo", + "impid": "twoImp", + "price": 5.64 + }, + "bidType": "video", + "bidder": "pubmatic" + } + ], + "expectedCacheables": [], + "defaultTTLs": { + "banner": 300, + "video": 3600, + "audio": 1800, + "native": 300 + }, + "targetDataIncludeWinners": false, + "targetDataIncludeBidderKeys": false, + "targetDataIncludeCacheBids": true, + "targetDataIncludeCacheVast": false +} \ No newline at end of file diff --git a/exchange/exchange.go b/exchange/exchange.go index 57e13644163..cf5ec9cc000 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -14,6 +14,7 @@ import ( "strings" "time" + uuid "github.com/gofrs/uuid" "github.com/prebid/prebid-server/stored_requests" "github.com/golang/glog" @@ -192,6 +193,23 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } + if !anyBidsReturned { + if debugLog != nil && debugLog.Enabled { + if rawUUID, err := uuid.NewV4(); err == nil { + debugLog.CacheKey = rawUUID.String() + + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, bidRequest, debugInfo, errs) + if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + debugLog.Data.Response = string(bidRespExtBytes) + } else { + debugLog.Data.Response = "Unable to marshal response ext for debugging" + } + } else { + errs = append(errs, err) + } + } + } + // Build the response return e.buildBidResponse(ctx, liveAdapters, adapterBids, bidRequest, adapterExtra, auc, bidResponseExt, errs) } diff --git a/exchange/exchangetest/debuglog_enabled_no_bids.json b/exchange/exchangetest/debuglog_enabled_no_bids.json new file mode 100644 index 00000000000..4823acf8f16 --- /dev/null +++ b/exchange/exchangetest/debuglog_enabled_no_bids.json @@ -0,0 +1,72 @@ +{ + "debugLog": { + "Enabled": true, + "CacheType": "xml", + "TTL": 3600, + "Data": { + "Request": "test request string", + "Headers": "test headers string", + "Response": "" + } + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withcategory": true + } + } + } + } + }, + "usersyncs": { + "appnexus": "123" + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBid": {} + } + } + }, + "response": { + "bids": {} + } +} \ No newline at end of file diff --git a/openrtb_ext/bid_response_video.go b/openrtb_ext/bid_response_video.go index 4c123498ec8..22661547ca7 100644 --- a/openrtb_ext/bid_response_video.go +++ b/openrtb_ext/bid_response_video.go @@ -14,7 +14,7 @@ type AdPod struct { } type VideoTargeting struct { - HbPb string `json:"hb_pb"` - HbPbCatDur string `json:"hb_pb_cat_dur"` - HbCacheID string `json:"hb_cache_id"` + HbPb string `json:"hb_pb,omitempty"` + HbPbCatDur string `json:"hb_pb_cat_dur,omitempty"` + HbCacheID string `json:"hb_cache_id,omitempty"` } From 2e9d8337d32971d4c8e03b3a68dd69d782610cb6 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Mon, 17 Aug 2020 12:09:51 -0400 Subject: [PATCH 169/318] Update the fallback GVL to last version (#1440) --- gdpr/vendorlist-fetching_test.go | 4 ++-- static/tcf1/fallback_gvl.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 031e564094c..484a0a54b41 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -165,7 +165,7 @@ func TestDefaultVendorList(t *testing.T) { list, err := fetcher(context.Background(), 12) assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) - assert.Equal(t, uint16(214), list.Version(), "Expected to fetch default version 214, got %d", list.Version()) + assert.Equal(t, uint16(215), list.Version(), "Expected to fetch default version 215, got %d", list.Version()) // Testing that we got the default vendorlist data, and not the version off the server. vendor := list.Vendor(12) @@ -227,7 +227,7 @@ func TestFallbackVendorListNoFetch(t *testing.T) { fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) list, err := fetcher(context.Background(), 2) assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) - assert.Equal(t, uint16(214), list.Version(), "Expected to fetch default version 214, got %d", list.Version()) + assert.Equal(t, uint16(215), list.Version(), "Expected to fetch default version 215, got %d", list.Version()) // Testing that we got the default vendorlist data, and not the version off the server. vendor := list.Vendor(12) diff --git a/static/tcf1/fallback_gvl.json b/static/tcf1/fallback_gvl.json index 86895a52362..9f1c8506b32 100644 --- a/static/tcf1/fallback_gvl.json +++ b/static/tcf1/fallback_gvl.json @@ -1 +1 @@ -{"vendorListVersion":214,"lastUpdated":"2020-08-06T16:00:35Z","purposes":[{"id":1,"name":"Information storage and access","description":"The storage of information, or access to information that is already stored, on your device such as advertising identifiers, device identifiers, cookies, and similar technologies."},{"id":2,"name":"Personalisation","description":"The collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as on other websites or apps, over time. Typically, the content of the site or app is used to make inferences about your interests, which inform future selection of advertising and/or content."},{"id":3,"name":"Ad selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver advertisements for you, and to measure the delivery and effectiveness of such advertisements. This includes using previously collected information about your interests to select ads, processing data about what advertisements were shown, how often they were shown, when and where they were shown, and whether you took any action related to the advertisement, including for example clicking an ad or making a purchase. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as websites or apps, over time."},{"id":4,"name":"Content selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver content for you, and to measure the delivery and effectiveness of such content. This includes using previously collected information about your interests to select content, processing data about what content was shown, how often or how long it was shown, when and where it was shown, and whether the you took any action related to the content, including for example clicking on content. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, such as websites or apps, over time."},{"id":5,"name":"Measurement","description":"The collection of information about your use of the content, and combination with previously collected information, used to measure, understand, and report on your usage of the service. This does not include personalisation, the collection of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, i.e. on other service, such as websites or apps, over time."}],"features":[{"id":1,"name":"Matching Data to Offline Sources","description":"Combining data from offline sources that were initially collected in other contexts."},{"id":2,"name":"Linking Devices","description":"Allow processing of a user's data to connect such user across multiple devices."},{"id":3,"name":"Precise Geographic Location Data","description":"Allow processing of a user's precise geographic location data in support of a purpose for which that certain third party has consent."}],"vendors":[{"id":8,"name":"Emerse Sverige AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.emerse.com/privacy-policy/"},{"id":9,"name":"AdMaxim Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.admaxim.com/admaxim-privacy-policy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":12,"name":"BeeswaxIO Corporation","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beeswax.com/privacy/"},{"id":28,"name":"TripleLift, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://triplelift.com/privacy/"},{"id":27,"name":"ADventori SAS","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adventori.com/with-us/legal-notice/"},{"id":25,"name":"Verizon Media EMEA Limited","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.verizonmedia.com/policies/ie/en/verizonmedia/privacy/index.html"},{"id":26,"name":"Venatus Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.venatusmedia.com/privacy/"},{"id":1,"name":"Exponential Interactive, Inc d/b/a VDX.tv","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://vdx.tv/privacy/"},{"id":6,"name":"AdSpirit GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adspirit.de/privacy"},{"id":30,"name":"BidTheatre AB","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.bidtheatre.com/privacy-policy"},{"id":24,"name":"Epsilon","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.conversantmedia.eu/legal/privacy-policy"},{"id":29,"name":"Etarget SE","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.etarget.sk/privacy.php","deletedDate":"2020-06-01T00:00:00Z"},{"id":39,"name":"ADITION technologies AG","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.adition.com/datenschutz"},{"id":11,"name":"Quantcast International Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.quantcast.com/privacy/"},{"id":15,"name":"Adikteev","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adikteev.com/privacy-policy-eng/"},{"id":4,"name":"Roq.ad Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.roq.ad/privacy-policy"},{"id":7,"name":"Vibrant Media Limited","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vibrantmedia.com/en/privacy-policy/"},{"id":2,"name":"Captify Technologies Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.captify.co.uk/privacy-policy/"},{"id":37,"name":"NEURAL.ONE","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://web.neural.one/privacy-policy/"},{"id":13,"name":"Sovrn Holdings Inc","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sovrn.com/sovrn-privacy/"},{"id":34,"name":"NEORY GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.neory.com/privacy.html"},{"id":32,"name":"Xandr, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.xandr.com/privacy/platform-privacy-policy/"},{"id":10,"name":"Index Exchange, Inc. ","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.indexexchange.com/privacy"},{"id":57,"name":"ADARA MEDIA UNLIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://adara.com/privacy-promise/"},{"id":63,"name":"Avocet Systems Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://avocet.io/privacy-portal"},{"id":51,"name":"xAd, Inc. dba GroundTruth","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.groundtruth.com/privacy-policy/"},{"id":49,"name":"TRADELAB","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://tradelab.com/en/privacy/"},{"id":45,"name":"Smart Adserver","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://smartadserver.com/end-user-privacy-policy/"},{"id":52,"name":"The Rubicon Project, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[3],"policyUrl":"http://www.rubiconproject.com/rubicon-project-yield-optimization-privacy-policy/"},{"id":71,"name":"Roku Advertising Services","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://docs.roku.com/published/userprivacypolicy/en/us"},{"id":79,"name":"MediaMath, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.mediamath.com/privacy-policy/"},{"id":91,"name":"Criteo SA","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.criteo.com/privacy/"},{"id":85,"name":"Crimtan Holdings Limited","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[1,3],"policyUrl":"https://crimtan.com/privacy/"},{"id":16,"name":"RTB House S.A.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.rtbhouse.com/privacy-center/services-privacy-policy/"},{"id":86,"name":"Scene Stealer Limited","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"http://scenestealer.tv/privacy-policy/"},{"id":94,"name":"Blis Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.blis.com/privacy/"},{"id":73,"name":"Simplifi Holdings Inc.","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2,3],"policyUrl":"https://simpli.fi/site-privacy-policy/"},{"id":33,"name":"ShareThis, Inc","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://sharethis.com/privacy/"},{"id":20,"name":"N Technologies Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://n.rich/privacy-notice"},{"id":55,"name":"Madison Logic, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.madisonlogic.com/privacy/"},{"id":53,"name":"Sirdata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.sirdata.com/privacy/"},{"id":69,"name":"OpenX","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.openx.com/legal/privacy-policy/"},{"id":98,"name":"GroupM UK Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.groupm.com/privacy-notice"},{"id":62,"name":"Justpremium BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://justpremium.com/privacy-policy/"},{"id":19,"name":"Intent Media, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://intentmedia.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":43,"name":"Vdopia DBA Chocolate Platform","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://chocolateplatform.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":36,"name":"RhythmOne DBA Unruly Group Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.rhythmone.com/privacy-policy"},{"id":80,"name":"Sharethrough, Inc","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://platform-cdn.sharethrough.com/privacy-policy"},{"id":81,"name":"PulsePoint, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pulsepoint.com/privacy-policy/website","deletedDate":"2020-07-06T00:00:00Z"},{"id":23,"name":"Amobee, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.amobee.com/trust/privacy-guidelines"},{"id":35,"name":"Purch Group, Inc.","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://www.purch.com/privacy-policy/","deletedDate":"2019-05-30T00:00:00Z"},{"id":3,"name":"affilinet","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.affili.net/de/footeritem/datenschutz","deletedDate":"2019-06-21T00:00:00Z"},{"id":74,"name":"Admotion SRL","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.admotion.com/policy/","deletedDate":"2019-07-24T00:00:00Z"},{"id":191,"name":"realzeit GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://realzeitmedia.com/privacy.html","deletedDate":"2019-04-29T00:00:00Z"},{"id":197,"name":"Switch Concepts Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.switchconcepts.com/privacy-policy","deletedDate":"2019-07-26T00:00:00Z"},{"id":390,"name":"Parsec Media Inc.","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,3],"policyUrl":"www.parsec.media/privacy-policy","deletedDate":"2019-06-27T00:00:00Z"},{"id":459,"name":"uppr GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://netzwerk.uppr.de/privacy-policy.do","deletedDate":"2019-06-17T00:00:00Z"},{"id":221,"name":"LEMO MEDIA GROUP LIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.lemomedia.com/terms.pdf","deletedDate":"2019-06-28T00:00:00Z"},{"id":478,"name":"RevLifter Ltd","purposeIds":[1],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.revlifter.com/privacy-policy","deletedDate":"2019-07-15T00:00:00Z"},{"id":500,"name":"Turbo","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.turboadv.com/white-rabbit-privacy-policy/","deletedDate":"2019-07-12T00:00:00Z"},{"id":68,"name":"Sizmek by Amazon","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.sizmek.com/privacy-policy/"},{"id":75,"name":"M32 Connect Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://m32.media/privacy-cookie-policy/"},{"id":17,"name":"Greenhouse Group BV (with its trademark LemonPI)","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.lemonpi.io/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":61,"name":"GumGum, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://gumgum.com/privacy-policy"},{"id":40,"name":"Active Agent (ADITION technologies AG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.active-agent.com/de/unternehmen/datenschutzerklaerung/"},{"id":76,"name":"PubMatic, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://pubmatic.com/privacy-policy/"},{"id":89,"name":"Tapad, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.tapad.com/eu-privacy-policy"},{"id":46,"name":"Skimbit Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://skimlinks.com/pages/privacy-policy"},{"id":66,"name":"adsquare GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adsquare.com/privacy"},{"id":105,"name":"Impression Desk Technologies Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://impressiondesk.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":41,"name":"Adverline","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.adverline.com/privacy/"},{"id":82,"name":"Smaato, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.smaato.com/privacy/"},{"id":60,"name":"Rakuten Marketing LLC","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://rakutenadvertising.com/legal-notices/services-privacy-policy/"},{"id":70,"name":"Yieldlab AG","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[3],"policyUrl":"http://www.yieldlab.de/meta-navigation/datenschutz/"},{"id":50,"name":"Adform","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://site.adform.com/privacy-center/platform-privacy/product-and-services-privacy-policy/"},{"id":48,"name":"NetSuccess, s.r.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inres.sk/pp/"},{"id":100,"name":"Fifty Technology Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://fifty.io/privacy-policy.php"},{"id":21,"name":"The Trade Desk","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.thetradedesk.com/general/privacy-policy"},{"id":110,"name":"Dynata LLC","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.opinionoutpost.co.uk/en-gb/policies/privacy"},{"id":42,"name":"Taboola Europe Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.taboola.com/privacy-policy"},{"id":112,"name":"Maytrics GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://maytrics.com/privacy.php","deletedDate":"2019-09-17T00:00:00Z"},{"id":77,"name":"comScore, Inc.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.scorecardresearch.com/privacy.aspx?newlanguage=1"},{"id":109,"name":"LoopMe Limited","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://loopme.com/privacy-policy/"},{"id":120,"name":"Eyeota Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.eyeota.com/privacy-center"},{"id":93,"name":"Adloox SA","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://adloox.com/disclaimer"},{"id":132,"name":"Teads ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.teads.com/privacy-policy/"},{"id":22,"name":"admetrics GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://admetrics.io/en/privacy_policy/"},{"id":102,"name":"Telaria SAS","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":108,"name":"Rich Audience Technologies SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://richaudience.com/privacy/"},{"id":18,"name":"Widespace AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.widespace.com/legal/privacy-policy-notice/"},{"id":122,"name":"Avid Media Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.avidglobalmedia.eu/privacy-policy.html"},{"id":97,"name":"LiveRamp, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.liveramp.com/service-privacy-policy/"},{"id":138,"name":"ConnectAd Realtime GmbH","purposeIds":[1,2],"legIntPurposeIds":[3,4],"featureIds":[],"policyUrl":"http://connectadrealtime.com/privacy/"},{"id":72,"name":"Nano Interactive GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.nanointeractive.com/privacy"},{"id":127,"name":"PIXIMEDIA SAS","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://piximedia.com/privacy/"},{"id":136,"name":"Str\u00f6er SSP GmbH (SSP)","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[2,3],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":111,"name":"Showheroes SE","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://showheroes.com/privacy/"},{"id":56,"name":"Confiant Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.confiant.com/privacy","deletedDate":"2020-05-18T00:00:00Z"},{"id":124,"name":"Teemo SA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://teemo.co/fr/confidentialite/"},{"id":154,"name":"YOC AG","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://yoc.com/privacy/"},{"id":38,"name":"Beemray Oy","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beemray.com/privacy-policy/","deletedDate":"2020-06-19T00:00:00Z"},{"id":101,"name":"MiQ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://wearemiq.com/privacy-policy/"},{"id":149,"name":"ADman Interactive SLU","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://admanmedia.com/politica.html?setLng=es"},{"id":151,"name":"Admedo Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[3],"policyUrl":"https://www.admedo.com/privacy-policy","deletedDate":"2020-07-17T00:00:00Z"},{"id":153,"name":"MADVERTISE MEDIA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://madvertise.com/en/gdpr/"},{"id":159,"name":"Underdog Media LLC ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://underdogmedia.com/privacy-policy/"},{"id":157,"name":"Seedtag Advertising S.L","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.seedtag.com/en/privacy-policy/"},{"id":145,"name":"Snapsort Inc., operating as Sortable","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://help.sortable.com/help/privacy-policy"},{"id":131,"name":"ID5 Technology SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.id5.io/privacy"},{"id":158,"name":"Reveal Mobile, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://revealmobile.com/privacy"},{"id":147,"name":"Adacado Technologies Inc. (DBA Adacado)","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adacado.com/privacy-policy-april-25-2018/"},{"id":130,"name":"NextRoll, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.nextroll.com/privacy"},{"id":129,"name":"IPONWEB GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.iponweb.com/privacy-policy/"},{"id":128,"name":"BIDSWITCH GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bidswitch.com/privacy-policy/"},{"id":168,"name":"EASYmedia GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://login.rtbmarket.com/gdpr"},{"id":164,"name":"Outbrain UK Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.outbrain.com/legal/privacy#privacy-policy"},{"id":144,"name":"district m inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://districtm.net/en/page/platforms-data-and-privacy-policy/"},{"id":163,"name":"Bombora Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://bombora.com/privacy"},{"id":173,"name":"Yieldmo, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.yieldmo.com/privacy/"},{"id":88,"name":"TreSensa, Inc.","purposeIds":[1,3],"legIntPurposeIds":[2,5],"featureIds":[1],"policyUrl":"https://www.tresensa.com/eu-privacy"},{"id":78,"name":"Flashtalking, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.flashtalking.com/privacypolicy/"},{"id":59,"name":"Sift Media, Inc","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.sift.co/privacy"},{"id":114,"name":"Sublime","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://ayads.co/privacy.php"},{"id":175,"name":"FORTVISION","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://fortvision.com/POC/index.html","deletedDate":"2019-08-09T00:00:00Z"},{"id":133,"name":"digitalAudience","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://digitalaudience.io/legal/privacy-cookies/"},{"id":14,"name":"Adkernel LLC","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://adkernel.com/privacy-policy/"},{"id":180,"name":"Thirdpresence Oy","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"http://www.thirdpresence.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":183,"name":"EMX Digital LLC","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://emxdigital.com/privacy/"},{"id":58,"name":"33Across","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.33across.com/privacy-policy"},{"id":140,"name":"Platform161","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://platform161.com/cookie-and-privacy-policy/"},{"id":90,"name":"Teroa S.A.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.e-planning.net/en/privacy.html"},{"id":141,"name":"1020, Inc. dba Placecast and Ericsson Emodo","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.emodoinc.com/privacy-policy/"},{"id":142,"name":"Media.net Advertising FZ-LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.media.net/en/privacy-policy"},{"id":209,"name":"Delta Projects AB","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[3],"policyUrl":"https://deltaprojects.com/data-collection-policy"},{"id":195,"name":"advanced store GmbH","purposeIds":[2,3],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.advanced-store.com/de/datenschutz/"},{"id":190,"name":"video intelligence AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.vi.ai/privacy-policy/"},{"id":84,"name":"Semasio GmbH","purposeIds":[],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"http://www.semasio.com/privacy-policy/"},{"id":65,"name":"Location Sciences AI Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.locationsciences.ai/privacy-policy/"},{"id":210,"name":"Zemanta, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1],"policyUrl":"http://www.zemanta.com/legal/privacy"},{"id":200,"name":"Tapjoy, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.tapjoy.com/legal/#privacy-policy"},{"id":188,"name":"Sellpoints Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://retargeter.com/service-privacy-policy/","deletedDate":"2019-09-17T00:00:00Z"},{"id":217,"name":"2KDirect, Inc. (dba iPromote)","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.ipromote.com/privacy-policy/"},{"id":156,"name":"Centro, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.centro.net/privacy-policy/"},{"id":194,"name":"Rezonence Limited","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://rezonence.com/privacy-policy/"},{"id":226,"name":"Publicis Media GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.publicismedia.de/datenschutz/"},{"id":198,"name":"SYNC","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://redirect.sync.tv/privacy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":227,"name":"ORTEC B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.ortecadscience.com/privacy-policy/"},{"id":225,"name":"Ligatus GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.ligatus.com/en/privacy-policy","deletedDate":"2020-06-19T00:00:00Z"},{"id":205,"name":"Adssets AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://adssets.com/policy/"},{"id":179,"name":"Collective Europe Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.collectiveuk.com/privacy.html"},{"id":31,"name":"Ogury Ltd.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://www.ogury.com/privacy-policy/"},{"id":92,"name":"1plusX AG","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.1plusx.com/privacy-policy/"},{"id":155,"name":"AntVoice","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.antvoice.com/en/privacypolicy/"},{"id":115,"name":"smartclip Europe GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://privacy-portal.smartclip.net/"},{"id":126,"name":"DoubleVerify Inc.\u200b","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.doubleverify.com/privacy/"},{"id":193,"name":"Mediasmart Mobile S.L.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://mediasmart.io/privacy/"},{"id":245,"name":"IgnitionOne","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.ignitionone.com/privacy-policy/","deletedDate":"2020-06-30T00:00:00Z"},{"id":213,"name":"emetriq GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.emetriq.com/datenschutz/"},{"id":244,"name":"Temelio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://temelio.com/vie-privee"},{"id":224,"name":"adrule mobile GmbH","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.adrule.net/de/datenschutz/"},{"id":174,"name":"A Million Ads Ltd","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.amillionads.com/privacy-policy"},{"id":192,"name":"remerge GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://remerge.io/privacy-policy.html"},{"id":232,"name":"Rockerbox, Inc","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"http://rockerbox.com/privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":256,"name":"Bounce Exchange, Inc","purposeIds":[1],"legIntPurposeIds":[2,4,5],"featureIds":[1,2],"policyUrl":"https://www.bouncex.com/privacy/"},{"id":234,"name":"ZBO Media","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zbo.media/mentions-legales/politique-de-confidentialite-service-publicitaire/"},{"id":246,"name":"Smartology Limited","purposeIds":[3],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://www.smartology.net/privacy-policy/"},{"id":241,"name":"OneTag Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.onetag.com/privacy/"},{"id":254,"name":"LiquidM Technology GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liquidm.com/privacy-policy/"},{"id":215,"name":"ARMIS SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://armis.tech/en/armis-personal-data-privacy-policy/"},{"id":167,"name":"Audiens S.r.l.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.audiens.com/privacy"},{"id":240,"name":"7Hops.com Inc. (ZergNet)","purposeIds":[],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://zergnet.com/privacy"},{"id":235,"name":"Bucksense Inc","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.bucksense.com/platform-privacy-policy/"},{"id":185,"name":"Bidtellect, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.bidtellect.com/privacy-policy/"},{"id":258,"name":"Adello Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.adello.com/privacy-policy/"},{"id":169,"name":"RTK.IO, Inc","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://www.rtk.io/privacy.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":208,"name":"Spotad","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.spotad.co/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":211,"name":"AdTheorent, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://adtheorent.com/privacy-policy"},{"id":229,"name":"Digitize New Media Ltd","purposeIds":[2,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitize.ie/online-privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":273,"name":"Bannerflow AB","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.bannerflow.com/privacy "},{"id":104,"name":"Sonobi, Inc","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"http://sonobi.com/privacy-policy/"},{"id":162,"name":"Unruly Group Ltd","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://unruly.co/privacy/"},{"id":249,"name":"Spolecznosci Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.spolecznosci.pl/polityka-prywatnosci"},{"id":125,"name":"Research Now Group, Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.valuedopinions.co.uk/privacy","deletedDate":"2019-09-17T00:00:00Z"},{"id":170,"name":"Goodway Group, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://goodwaygroup.com/privacy-policy/"},{"id":160,"name":"Netsprint SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://netsprint.eu/privacy.html"},{"id":189,"name":"Intowow Innovation Ltd.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.intowow.com/privacy/","deletedDate":"2019-08-12T00:00:00Z"},{"id":279,"name":"Mirando GmbH & Co KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://wwwmirando.de/datenschutz/"},{"id":269,"name":"Sanoma Media Finland","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://sanoma.fi/tietoa-meista/tietosuoja/","deletedDate":"2019-08-07T00:00:00Z"},{"id":276,"name":"Viralize SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://viralize.com/privacy-policy"},{"id":87,"name":"Genius Sports Media Limited","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[2,3],"policyUrl":"https://www.geniussports.com/privacy-policy"},{"id":182,"name":"Collective, Inc. dba Visto","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vistohub.com/privacy-policy/","deletedDate":"2019-07-26T00:00:00Z"},{"id":255,"name":"Onnetwork Sp. z o.o.","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.onnetwork.tv/pp_services.php"},{"id":203,"name":"Revcontent, LLC","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://intercom.help/revcontent2/en/articles/2290675-revcontent-s-privacy-policy"},{"id":260,"name":"RockYou, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,5],"featureIds":[3],"policyUrl":"https://rockyou.com/privacy-policy/","deletedDate":"2019-08-09T00:00:00Z"},{"id":237,"name":"LKQD, a division of Nexstar Digital, LLC.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.lkqd.com/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":274,"name":"Golden Bees","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.goldenbees.fr/en/privacy-charter/"},{"id":280,"name":"Spot.IM LTD","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.spot.im/privacy/"},{"id":239,"name":"Triton Digital Canada Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.tritondigital.com/privacy-policies"},{"id":177,"name":"plista GmbH","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.plista.com/about/privacy/"},{"id":201,"name":"TimeOne","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://privacy.timeonegroup.com/en/","deletedDate":"2020-05-15T00:00:00Z"},{"id":150,"name":"Inskin Media LTD","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.inskinmedia.com/privacy-policy.html"},{"id":252,"name":"Jaduda GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.jadudamobile.com/datenschutzerklaerung/"},{"id":248,"name":"Converge-Digital","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://converge-digital.com/privacy-policy/"},{"id":161,"name":"Smadex SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://smadex.com/end-user-privacy-policy/"},{"id":285,"name":"Comcast International France SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.freewheel.com/privacy-policy"},{"id":228,"name":"McCann Discipline LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.primis.tech/privacy-policy/"},{"id":299,"name":"AdClear GmbH","purposeIds":[1,5],"legIntPurposeIds":[2,3,4],"featureIds":[1,2],"policyUrl":"https://www.adclear.de/datenschutzerklaerung/"},{"id":277,"name":"Codewise VL Sp. z o.o. Sp. k","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://voluumdsp.com/end-user-privacy-policy/"},{"id":259,"name":"ADYOULIKE SA","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.adyoulike.com/privacy_policy.php"},{"id":272,"name":"A.Mob","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.we-are-adot.com/privacy-policy/"},{"id":230,"name":"Steel House, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://steelhouse.com/privacy-policy/"},{"id":253,"name":"Improve Digital BV","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.improvedigital.com/platform-privacy-policy"},{"id":304,"name":"On Device Research Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://s.on-device.com/privacyPolicy"},{"id":314,"name":"Keymantics","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.keymantics.com/assets/privacy-policy.pdf"},{"id":257,"name":"R-TARGET","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"http://www.r-target.com/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":317,"name":"mainADV Srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.mainad.com/privacy-policy/"},{"id":278,"name":"Integral Ad Science, Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://integralads.com/privacy-policy/"},{"id":291,"name":"Qwertize","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.qwertize.com/en/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":295,"name":"Sojern, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.sojern.com/privacy/product-privacy-policy/"},{"id":315,"name":"Celtra, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.celtra.com/privacy-policy/"},{"id":165,"name":"SpotX, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.spotx.tv/privacy-policy/"},{"id":47,"name":"ADMAN - Phaistos Networks, S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adman.gr/privacy"},{"id":134,"name":"SMARTSTREAM.TV GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://www.smartstream.tv/en/productprivacy"},{"id":325,"name":"Knorex","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.knorex.com/privacy"},{"id":316,"name":"Gamned","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.gamned.com/privacy-policy/"},{"id":318,"name":"Accorp Sp. z o.o.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"http://www.instytut-pollster.pl/privacy-policy/"},{"id":199,"name":"ADUX","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adux.com/donnees-personelles/"},{"id":236,"name":"PowerLinks Media Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[3],"policyUrl":"https://www.powerlinks.com/privacy-policy/"},{"id":294,"name":"Jivox Corporation","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.jivox.com/privacy"},{"id":143,"name":"Connatix Native Exchange Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://connatix.com/privacy-policy/"},{"id":297,"name":"Polar Mobile Group Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://privacy.polar.me"},{"id":319,"name":"Clipcentric, Inc.","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://clipcentric.com/privacy.bhtml"},{"id":290,"name":"Readpeak Oy","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://readpeak.com/privacy-policy/"},{"id":323,"name":"DAZN Media Services Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.goal.com/en-gb/legal/privacy-policy"},{"id":119,"name":"Fusio by S4M","purposeIds":[1,2,5],"legIntPurposeIds":[3],"featureIds":[1,3],"policyUrl":"http://www.s4m.io/privacy-policy/"},{"id":302,"name":"Mobile Professionals BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mobpro.com/privacy.html"},{"id":212,"name":"usemax advertisement (Emego GmbH)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.usemax.de/?l=privacy"},{"id":264,"name":"Adobe Advertising Cloud","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.adobe.com/privacy/experience-cloud.html"},{"id":44,"name":"The ADEX GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://theadex.com/privacy-opt-out/"},{"id":282,"name":"Welect GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.welect.de/datenschutz"},{"id":238,"name":"StackAdapt","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.stackadapt.com/privacy"},{"id":284,"name":"WEBORAMA","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://weborama.com/privacy_en/"},{"id":148,"name":"Liveintent Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://liveintent.com/services-privacy-policy/"},{"id":64,"name":"DigiTrust / IAB Tech Lab","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitru.st/privacy-policy/"},{"id":301,"name":"zeotap GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://zeotap.com/privacy_policy"},{"id":275,"name":"TabMo SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://static.tabmo.io.s3.amazonaws.com/privacy-policy/index.html"},{"id":310,"name":"Adevinta Spain S.L.U.","purposeIds":[],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"https://www.adevinta.com/about/privacy/"},{"id":139,"name":"Permodo GmbH","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://permodo.com/de/privacy.html"},{"id":326,"name":"AdTiming Technology Company Limited","purposeIds":[3,5],"legIntPurposeIds":[1,2,4],"featureIds":[],"policyUrl":"http://www.adtiming.com/en/privacypolicy.html"},{"id":262,"name":"Fyber ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.fyber.com/legal/privacy-policy/"},{"id":331,"name":"ad6media","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.ad6media.fr/privacy"},{"id":345,"name":"The Kantar Group Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.kantar.com/cookies-policies"},{"id":308,"name":"Rockabox Media Ltd","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[],"policyUrl":"http://scoota.com/privacy-policy"},{"id":270,"name":"Marfeel Solutions, SL","purposeIds":[],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.marfeel.com/privacy-policy/"},{"id":333,"name":"InMobi Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":202,"name":"Telaria, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":328,"name":"Gemius SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.gemius.com/cookie-policy.html"},{"id":281,"name":"Wizaly","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.wizaly.com/terms-of-use#privacy-policy"},{"id":354,"name":"Apester Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://apester.com/privacy-policy/"},{"id":320,"name":"Adelphic LLC","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://adelphic.com/platform/privacy/"},{"id":359,"name":"AerServ LLC","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":265,"name":"Instinctive, Inc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://instinctive.io/privacy"},{"id":349,"name":"Optomaton UG","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://optomaton.com/privacy.html"},{"id":288,"name":"Video Media Groep B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://www.videomediagroup.com/wp-content/uploads/2016/01/Privacy-policy-VMG.pdf","deletedDate":"2019-09-17T00:00:00Z"},{"id":266,"name":"Digilant Spain, SLU","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.digilant.com/es/politica-privacidad/"},{"id":339,"name":"Vuble","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vuble.tv/us/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":303,"name":"Orion Semantics","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://static.orion-semantics.com/privacy.html"},{"id":261,"name":"Signal Digital Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.signal.co/privacy-policy/"},{"id":83,"name":"Visarity Technologies GmbH","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://primo.design/docs/PrivacyPolicyPrimo.html"},{"id":343,"name":"DIGITEKA Technologies","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.ultimedia.com/POLICY.html"},{"id":330,"name":"Linicom","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.linicom.com/privacy/","deletedDate":"2020-06-08T00:00:00Z"},{"id":231,"name":"AcuityAds Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.acuityads.com/corporate-privacy-policy.html"},{"id":216,"name":"Mindlytix SAS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://mindlytix.com/privacy/"},{"id":360,"name":"Permutive Technologies, Inc.","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[1,2],"policyUrl":"https://permutive.com/privacy","deletedDate":"2020-03-31T00:00:00Z"},{"id":361,"name":"Permutive","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://permutive.com/privacy"},{"id":311,"name":"Mobfox US LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobfox.com/privacy-policy/"},{"id":358,"name":"MGID Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mgid.com/privacy-policy"},{"id":152,"name":"Meetrics GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.meetrics.com/en/data-privacy/"},{"id":251,"name":"Yieldlove GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"http://www.yieldlove.com/cookie-policy"},{"id":344,"name":"My6sense Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[2,4],"featureIds":[],"policyUrl":"https://my6sense.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":347,"name":"Ezoic Inc.","purposeIds":[2,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.ezoic.com/terms/"},{"id":218,"name":"Bigabid Media ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.bigabid.com/privacy-policy"},{"id":350,"name":"Free Stream Media Corp. dba Samba TV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":351,"name":"Samba TV UK Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":341,"name":"Somo Audience Corp","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"https://somoaudience.com/legal/","deletedDate":"2020-07-06T00:00:00Z"},{"id":380,"name":"Vidoomy Media SL","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"http://vidoomy.com/privacy-policy.html"},{"id":378,"name":"communicationAds GmbH & Co. KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.communicationads.net/aboutus/privacy/"},{"id":369,"name":"Getintent USA, inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://getintent.com/privacy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":184,"name":"mediarithmics SAS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mediarithmics.com/en-us/content/privacy-policy"},{"id":368,"name":"VECTAURY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vectaury.io/en/personal-data"},{"id":373,"name":"Nielsen Marketing Cloud","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"http://www.nielsen.com/us/en/privacy-statement/exelate-privacy-policy.html"},{"id":214,"name":"Digital Control GmbH & Co. KG","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://advolution.de/privacy.php","deletedDate":"2020-05-06T00:00:00Z"},{"id":388,"name":"numberly","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://numberly.com/en/privacy/"},{"id":250,"name":"Qriously Ltd","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.brandwatch.com/legal/qriously-privacy-notice/"},{"id":223,"name":"Audience Trading Platform Ltd.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://atp.io/privacy-policy"},{"id":384,"name":"Pixalate, Inc.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"http://pixalate.com/privacypolicy/","deletedDate":"2019-11-08T00:00:00Z"},{"id":387,"name":"Triapodi Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appreciate.mobi/page.html#/end-user-privacy-policy"},{"id":312,"name":"Exactag GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.exactag.com/en/data-privacy/"},{"id":178,"name":"Hybrid Theory","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://hybridtheory.com/privacy-policy/"},{"id":377,"name":"AddApptr GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.addapptr.com/data-privacy"},{"id":382,"name":"The Reach Group GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://trg.de/en/privacy-statement/"},{"id":206,"name":"Hybrid Adtech GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://hybrid.ai/data_protection_policy"},{"id":403,"name":"Mobusi Mobile Advertising S.L.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobusi.com/privacy.en.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":385,"name":"Oracle Data Cloud","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html"},{"id":404,"name":"Duplo Media AS","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.easy-ads.com/privacypolicy.htm"},{"id":242,"name":"twiago GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.twiago.com/datenschutz/"},{"id":376,"name":"Pocketmath Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pocketmath.com/privacy-policy"},{"id":402,"name":"Effiliation","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://inter.effiliation.com/politique-confidentialite.html"},{"id":413,"name":"Eulerian Technologies","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.eulerian.com/en/privacy/"},{"id":400,"name":"Whenever Media Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.whenevermedia.com/privacy-policy","deletedDate":"2019-07-29T00:00:00Z"},{"id":171,"name":"Webedia","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webedia-group.com/site/privacy-policy","deletedDate":"2020-07-01T00:00:00Z"},{"id":398,"name":"Yormedia Solutions Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.yormedia.com/privacy-and-cookies-notice/","deletedDate":"2019-08-06T00:00:00Z"},{"id":415,"name":"Seenthis AB","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://seenthis.co/privacy-notice-2018-04-18.pdf"},{"id":263,"name":"Nativo, Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.nativo.com/interest-based-ads"},{"id":329,"name":"Browsi Mobile Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://gobrowsi.com/browsi-privacy-policy/"},{"id":389,"name":"Bidmanagement GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adspert.net/en/privacy/","deletedDate":"2020-07-01T00:00:00Z"},{"id":337,"name":"SheMedia, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shemedia.com/ad-services-privacy-policy"},{"id":422,"name":"Brand Metrics Sweden AB","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://collector.brandmetrics.com/brandmetrics_privacypolicy.pdf"},{"id":421,"name":"LeftsnRight, Inc. dba LIQWID","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liqwid.solutions/privacy-policy","deletedDate":"2020-06-30T00:00:00Z"},{"id":426,"name":"TradeTracker","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[2],"policyUrl":"https://tradetracker.com/privacy-policy/","deletedDate":"2019-08-21T00:00:00Z"},{"id":394,"name":"AudienceProject Aps","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://privacy.audienceproject.com"},{"id":287,"name":"Avazu Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4],"featureIds":[3],"policyUrl":"http://avazuinc.com/opt-out/","deletedDate":"2020-08-03T00:00:00Z"},{"id":243,"name":"Cloud Technologies S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cloudtechnologies.pl/en/internet-advertising-privacy-policy"},{"id":113,"name":"iotec global Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.iotecglobal.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":338,"name":"dunnhumby Germany GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.sociomantic.com/privacy/en/","deletedDate":"2020-07-17T00:00:00Z"},{"id":405,"name":"IgnitionAi Ltd","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[2],"policyUrl":"https://www.isitelab.io/default.aspx","deletedDate":"2020-07-03T00:00:00Z"},{"id":416,"name":"Commanders Act","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.commandersact.com/en/privacy/"},{"id":434,"name":"DynAdmic","purposeIds":[1,3],"legIntPurposeIds":[2,4],"featureIds":[1,3],"policyUrl":"http://eu.dynadmic.com/privacy-policy/"},{"id":435,"name":"SINGLESPOT SAS ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.singlespot.com/privacy_policy?locale=fr"},{"id":409,"name":"Arrivalist Co.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[1,2],"policyUrl":"https://www.arrivalist.com/privacy"},{"id":321,"name":"Ziff Davis LLC","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.ziffdavis.com/privacy-policy"},{"id":436,"name":"INVIBES GROUP","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[1,2,3],"policyUrl":"http://www.invibes.com/terms"},{"id":442,"name":"R-Advertising","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-20T00:00:00Z"},{"id":362,"name":"Myntelligence S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://myntelligence.com/privacy-page/"},{"id":418,"name":"PROXISTORE","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://www.proxistore.com/common/en/cgv"},{"id":449,"name":"Mobile Journey B.V.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://mobilejourney.com/Privacy-Policy","deletedDate":"2019-09-05T00:00:00Z"},{"id":443,"name":"Tradedoubler AB","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-13T00:00:00Z"},{"id":429,"name":"Signals","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://signalsdata.com/platform-cookie-policy/"},{"id":335,"name":"Beachfront Media LLC","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://beachfront.com/privacy-policy/"},{"id":407,"name":"Publishers Internationale Pty Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pi-rate.com.au/privacy.html","deletedDate":"2019-11-08T00:00:00Z"},{"id":427,"name":"Proxi.cloud Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://proxi.cloud/info/privacy-policy/"},{"id":374,"name":"Bmind a Sales Maker Company, S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bmind.es/legal-notice/"},{"id":438,"name":"INVIDI technologies AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.invidi.com/wp-content/uploads/2020/02/ad-tech-services-privacy-policy.pdf"},{"id":450,"name":"Neodata Group srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.neodatagroup.com/en/security-policy"},{"id":452,"name":"Innovid Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.innovid.com/privacy-policy"},{"id":444,"name":"Playbuzz Ltd (aka EX.CO)","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://ex.co/privacy-policy/"},{"id":412,"name":"Cxense ASA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.cxense.com/about-us/privacy-policy"},{"id":454,"name":"Adimo","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://adimo.co/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":455,"name":"GDMServices, Inc. d/b/a FiksuDSP","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://fiksu.com/privacy-policy/"},{"id":298,"name":"Cuebiq Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.cuebiq.com/privacypolicy/","deletedDate":"2019-08-30T00:00:00Z"},{"id":423,"name":"travel audience GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://travelaudience.com/product-privacy-policy/"},{"id":397,"name":"Demandbase, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.demandbase.com/privacy-policy/"},{"id":381,"name":"Solocal","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://frontend.adhslx.com/privacy.html?"},{"id":425,"name":"ADRINO Sp. z o.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.adrino.pl/ciasteczkowa-polityka/","deletedDate":"2019-09-05T00:00:00Z"},{"id":365,"name":"Forensiq LLC","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1,3],"policyUrl":"https://impact.com/privacy-policy/"},{"id":447,"name":"Adludio Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adludio.com/privacy-policy/"},{"id":410,"name":"Adtelligent Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtelligent.com/privacy-policy/"},{"id":137,"name":"Str\u00f6er SSP GmbH (DSP)","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":395,"name":"PREX Programmatic Exchange GmbH&Co KG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[],"policyUrl":"http://www.programmatic-exchange.com/privacy","deletedDate":"2020-07-03T00:00:00Z"},{"id":462,"name":"Bidstack Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[2],"policyUrl":"https://www.bidstack.com/privacy-policy/"},{"id":466,"name":"TACTIC\u2122 Real-Time Marketing AS","purposeIds":[],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://tacticrealtime.com/privacy/"},{"id":340,"name":"Yieldr UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.yieldr.com/privacy"},{"id":336,"name":"Telecoming S.A.","purposeIds":[3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.telecoming.com/privacy-policy/"},{"id":430,"name":"Ad Unity Ltd","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"http://www.adunity.com/privacy-policy.html","deletedDate":"2019-08-13T00:00:00Z"},{"id":346,"name":"Cybba, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://cybba.com/about/legal/data-processing-agreement/","deletedDate":"2020-08-03T00:00:00Z"},{"id":469,"name":"Zeta Global","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://zetaglobal.com/privacy-policy/"},{"id":440,"name":"DEFINE MEDIA GMBH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.definemedia.de/datenschutz-conative/"},{"id":375,"name":"Affle International","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://affle.com/privacy-policy "},{"id":196,"name":"AdElement Media Solutions Pvt Ltd","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"http://adelement.com/privacy-policy.html"},{"id":268,"name":"Social Tokens Ltd. ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://woobi.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":475,"name":"TAPTAP Digital SL","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1,2,3],"policyUrl":"http://www.taptapnetworks.com/privacy_policy/"},{"id":474,"name":"hbfsTech","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.hbfstech.com/fr/privacy.html"},{"id":448,"name":"Targetspot Belgium SPRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://marketing.targetspot.com/Targetspot/Legal/TargetSpot%20Privacy%20Policy%20-%20June%202018.pdf"},{"id":428,"name":"Internet BillBoard a.s.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.ibillboard.com/en/privacy-information/"},{"id":461,"name":"B2B Media Group EMEA GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selfcampaign.com/static/privacy","deletedDate":"2019-08-14T00:00:00Z"},{"id":476,"name":"HIRO Media Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"http://hiro-media.com/privacy.php"},{"id":480,"name":"pilotx.tv","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[1,2,3],"policyUrl":"https://pilotx.tv/privacy/"},{"id":366,"name":"CerebroAd.com s.r.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.cerebroad.com/privacy-policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":392,"name":"Str\u00f6er Mobile Performance GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[3],"policyUrl":"https://stroeermobileperformance.com/?dl=privacy"},{"id":357,"name":"Totaljobs Group Ltd ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.totaljobs.com/privacy-policy"},{"id":486,"name":"Madington","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://delivered-by-madington.com/dat-privacy-policy/"},{"id":468,"name":"NeuStar, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://www.home.neustar/privacy"},{"id":458,"name":"AdColony, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"adcolony.com/privacy-policy/"},{"id":489,"name":"YellowHammer Media Group","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.yhmg.com/privacy-policy/","deletedDate":"2019-11-27T00:00:00Z"},{"id":293,"name":"SpringServe, LLC","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://springserve.com/privacy-policy/"},{"id":484,"name":"STRIATUM SAS","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://adledge.com/data-privacy/"},{"id":493,"name":"Carbon (AI) Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://carbonrmp.com/privacy.html"},{"id":495,"name":"Arcspire Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://public.arcspire.io/privacy.pdf"},{"id":496,"name":"Automattic Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://en.blog.wordpress.com/2017/12/04/updated-privacy-policy/"},{"id":424,"name":"KUPONA GmbH","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.kupona.de/dsgvo/"},{"id":408,"name":"Fidelity Media","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://fidelity-media.com/privacy-policy/"},{"id":473,"name":"Sub2 Technologies Ltd","purposeIds":[3,4,5],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.sub2tech.com/privacy-policy/"},{"id":467,"name":"Haensel AMS GmbH","purposeIds":[3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://haensel-ams.com/data-privacy/"},{"id":490,"name":"PLAYGROUND XYZ EMEA LTD","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://playground.xyz/privacy"},{"id":464,"name":"Oracle AddThis","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.addthis.com/privacy/privacy-policy/","deletedDate":"2020-02-12T00:00:00Z"},{"id":491,"name":"Triboo Data Analytics","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shinystat.com/it/informativa_privacy_generale.html"},{"id":499,"name":"PurposeLab, LLC","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://purposelab.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":502,"name":"NEXD","purposeIds":[5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://nexd.com/privacy-policy"},{"id":465,"name":"Schibsted Product and Tech UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.schibsted.com/","deletedDate":"2019-07-26T00:00:00Z"},{"id":497,"name":"Little Big Data sp.z.o.o.","purposeIds":[1,2,4],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://dtxngr.com/legal/"},{"id":492,"name":"LotaData, Inc.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1],"policyUrl":"https://lotadata.com/privacy_policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":512,"name":"PubNative GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://pubnative.net/privacy-notice/"},{"id":471,"name":"FlexOffers.com, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.flexoffers.com/privacy-policy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":494,"name":"Cablato Limited","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://cablato.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":516,"name":"Pexi B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://pexi.nl/privacy-policy/"},{"id":507,"name":"AdsWizz Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://www.adswizz.com/our-privacy-policy/"},{"id":482,"name":"UberMedia, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ubermedia.com/summary-of-privacy-policy/"},{"id":505,"name":"Shopalyst Inc","purposeIds":[1,2],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shortlyst.com/eu/privacy_terms.html"},{"id":517,"name":"SunMedia ","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2],"policyUrl":"https://www.sunmedia.tv/en/cookies"},{"id":518,"name":"Accelerize Inc.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://getcake.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":511,"name":"Admixer EU GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://admixer.com/privacy/"},{"id":479,"name":"INFINIA MOBILE S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.infiniamobile.com/privacy_policy"},{"id":513,"name":"Shopstyle","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shopstyle.co.uk/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":509,"name":"ATG Ad Tech Group GmbH","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ad-tech-group.com/privacy-policy/"},{"id":521,"name":"netzeffekt GmbH","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.netzeffekt.de/en/imprint"},{"id":487,"name":"nugg.ad GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1],"policyUrl":"https://www.nugg.ad/en/privacy/general-information.html","deletedDate":"2019-10-03T00:00:00Z"},{"id":515,"name":"ZighZag","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zighzag.com/privacy"},{"id":520,"name":"ChannelSight ","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.channelsight.com/privacypolicy/"},{"id":524,"name":"The Ozone Project Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://ozoneproject.com/privacy-policy"},{"id":529,"name":"Fidzup","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.fidzup.com/en/privacy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":528,"name":"Kayzen","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://kayzen.io/data-privacy-policy"},{"id":527,"name":"Jampp LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://jampp.com/privacy.html"},{"id":506,"name":"salesforce.com, inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.salesforce.com/company/privacy/"},{"id":534,"name":"SmartyAds Inc.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://smartyads.com/privacy-policy"},{"id":535,"name":"INNITY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.innity.com/privacy-policy.php"},{"id":514,"name":"Uprival LLC","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://uprival.com/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":522,"name":"Tealium Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://tealium.com/privacy-policy/"},{"id":530,"name":"Near Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://near.co/privacy"},{"id":539,"name":"AdDefend GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.addefend.com/en/privacy-policy/"},{"id":501,"name":"Alliance Gravity Data Media","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.alliancegravity.com/politiquedeprotectiondesdonneespersonnelles"},{"id":519,"name":"Chargeads","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.chargeplatform.com/privacy"},{"id":523,"name":"X-Mode Social, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://xmode.io/privacy-policy.html"},{"id":537,"name":"RUN, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.runads.com/privacy-policy"},{"id":531,"name":"Smartclip Hispania SL","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://rgpd-smartclip.com/"},{"id":536,"name":"GlobalWebIndex","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"http://legal.trendstream.net/non-panellist_privacy_policy"},{"id":542,"name":"Densou Trading Desk ApS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://densou.dk/Policy.html","deletedDate":"2020-01-21T00:00:00Z"},{"id":525,"name":"PUB OCEAN LIMITED","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://rta.pubocean.com/privacy-policy/","deletedDate":"2019-10-03T00:00:00Z"},{"id":544,"name":"Kochava Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://www.kochava.com/support-privacy/"},{"id":543,"name":"PaperG, Inc. dba Thunder Industries","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.makethunder.com/privacy"},{"id":334,"name":"Cydersoft","purposeIds":[],"legIntPurposeIds":[1,2,3,4],"featureIds":[2,3],"policyUrl":"http://www.videmob.com/privacy.html"},{"id":551,"name":"Illuma Technology Limited","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.weareilluma.com/endddd","deletedDate":"2019-11-14T00:00:00Z"},{"id":540,"name":"Tunnl BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://tunnl.com/privacy.html","deletedDate":"2019-12-20T00:00:00Z"},{"id":547,"name":"Video Reach","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.videoreach.de/about/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":546,"name":"Smart Traffik","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://okube-attribution.com/politique-de-confidentialite/"},{"id":541,"name":"DeepIntent, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.deepintent.com/privacypolicy"},{"id":545,"name":"Reignn Platform Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://reignn.com/user-privacy-policy"},{"id":439,"name":"Bit Q Holdings Limited","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.rippll.com/privacy"},{"id":553,"name":"Adhese","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://adhese.com/privacy-and-cookie-policy"},{"id":556,"name":"adhood.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://v3.adhood.com/en/site/politikavekurallar/gizlilik.php?lang=en"},{"id":550,"name":"Happydemics","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.iubenda.com/privacy-policy/69056167/full-legal"},{"id":560,"name":"Leiki Ltd.","purposeIds":[1,2,3],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"http://www.leiki.com/privacy","deletedDate":"2020-01-07T00:00:00Z"},{"id":554,"name":"RMSi Radio Marketing Service interactive GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.rms.de/datenschutz/"},{"id":498,"name":"Dr. Banner","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://drbanner.com/privacypolicy_en/"},{"id":565,"name":"Adobe Audience Manager","purposeIds":[1,2,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adobe.com/privacy/policy.html"},{"id":118,"name":"Drawbridge, Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.drawbridge.com/privacy/","deletedDate":"2020-03-06T00:00:00Z"},{"id":572,"name":"CHEQ AI TECHNOLOGIES LTD.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.cheq.ai/privacy"},{"id":571,"name":"ViewPay","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://viewpay.tv/mentions-legales/"},{"id":568,"name":"Jointag S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.jointag.com/privacy/kariboo/publisher/third/"},{"id":570,"name":"Czech Publisher Exchange z.s.p.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cpex.cz/pro-uzivatele/ochrana-soukromi/"},{"id":559,"name":"Otto (GmbH & Co KG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2],"policyUrl":"https://www.otto.de/shoppages/service/datenschutz"},{"id":548,"name":"LBC France","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.leboncoin.fr/dc/cookies","deletedDate":"2020-04-23T00:00:00Z"},{"id":569,"name":"Kairos Fire","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.kairosfire.com/privacy"},{"id":577,"name":"Neustar on behalf of The Procter & Gamble Company","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pg.com/privacy/english/privacy_statement.shtml"},{"id":590,"name":"Sourcepoint Technologies, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.sourcepoint.com/privacy-policy"},{"id":587,"name":"Localsensor B.V.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.localsensor.com/privacy.html"},{"id":578,"name":"MAIRDUMONT NETLETIX GmbH&Co. KG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mairdumont-netletix.com/datenschutz"},{"id":580,"name":"Goldbach Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://goldbach.com/ch/de/datenschutz"},{"id":593,"name":"Programatica de publicidad S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://datmean.com/politica-privacidad/"},{"id":574,"name":"Realeyes OU","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://realview.realeyesit.com/privacy"},{"id":581,"name":"Mobilewalla, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.mobilewalla.com/business-services-privacy-policy"},{"id":598,"name":"audio content & control GmbH","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://www.audio-cc.com/audiocc_privacy_policy.pdf"},{"id":596,"name":"InsurAds Technologies SA.","purposeIds":[3],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.insurads.com/privacy.html"},{"id":576,"name":"StartApp Inc.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://www.startapp.com/policy/privacy-policy/","deletedDate":"2020-04-23T00:00:00Z"},{"id":592,"name":"Colpirio.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy-policy.colpirio.com/en/","deletedDate":"2020-03-18T00:00:00Z"},{"id":549,"name":"Bandsintown Amplified LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://corp.bandsintown.com/privacy"},{"id":597,"name":"Better Banners A/S","purposeIds":[],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://betterbanners.com/en/privacy"},{"id":601,"name":"WebAds B.V","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.webads.eu/"},{"id":599,"name":"Maximus Live LLC","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://maximusx.com/privacy-policy/"},{"id":604,"name":"Join","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.teamjoin.fr/privacy.html","deletedDate":"2020-04-23T00:00:00Z"},{"id":606,"name":"Impactify ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://impactify.io/privacy-policy/"},{"id":608,"name":"News and Media Holding, a.s.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.newsandmedia.sk/gdpr/"},{"id":602,"name":"Online Solution Int Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://adsafety.net/privacy.html"},{"id":612,"name":"Adnami Aps","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adnami.io/privacy","deletedDate":"2020-03-17T00:00:00Z"},{"id":591,"name":"Consumable, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://consumable.com/privacy-policy.html"},{"id":614,"name":"Market Resource Partners LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.mrpfd.com/privacy-policy/"},{"id":615,"name":"Adsolutions BV","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adsolutions.com/privacy-policy/"},{"id":607,"name":"ucfunnel Co., Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.ucfunnel.com/privacy-policy"},{"id":609,"name":"Predicio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.predic.io/privacy"},{"id":617,"name":"Onfocus (Adagio)","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adagio.io/privacy"},{"id":620,"name":"Blue","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.getblue.io/privacy/"},{"id":610,"name":"Azerion Holding B.V.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://azerion.com/business/privacy.html"},{"id":621,"name":"Seznam.cz, a.s.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://www.seznam.cz/ochranaudaju"},{"id":624,"name":"Norstat AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.norstatpanel.com/en/data-protection"},{"id":623,"name":"Adprime Media Inc. ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adprimehealth.com/privacy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":95,"name":"Lotame Solutions, inc","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[2],"policyUrl":"https://www.lotame.com/about-lotame/privacy/lotame-corporate-websites-privacy-policy/"},{"id":618,"name":"BEINTOO SPA","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.beintoo.com/privacy-cookie-policy/"},{"id":619,"name":"Capitaldata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.capitaldata.fr/privacy"},{"id":625,"name":"BILENDI SA","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.maximiles.com/privacy-policy"},{"id":628,"name":": Tappx","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.tappx.com/en/privacy-policy/"},{"id":626,"name":"Hivestack Inc.","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://hivestack.com/privacy-policy"},{"id":631,"name":"Relay42 Netherlands B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://relay42.com/privacy"},{"id":627,"name":"D-Edge","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.d-edge.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":644,"name":"Gamoshi LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.gamoshi.com/privacy-policy"},{"id":639,"name":"Smile Wanted Group","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.smilewanted.com/privacy.php"},{"id":635,"name":"WebMediaRM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webmediarm.com/vie_privee_et_opposition_en.php"},{"id":579,"name":"Ve Global","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.ve.com/privacy-policy"},{"id":645,"name":"Noster Finance S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.finect.com/terminos-legales/politica-de-cookies"},{"id":653,"name":"Smartme Analytics","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"http://smartmeapp.com/info/smartme/aviso_legal.php","deletedDate":"2020-07-03T00:00:00Z"},{"id":613,"name":"Adserve.zone / Artworx AS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adserve.zone/adserveprivacypolicy.html"},{"id":573,"name":"Dailymotion SA","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2],"policyUrl":"https://www.dailymotion.com/legal/privacy"},{"id":652,"name":"Skaze","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.skaze.fr/rgpd/"},{"id":646,"name":"Notify","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"https://notify-group.com/en/mentions-legales/"},{"id":648,"name":"TrueData Solutions, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.truedata.co/privacy-policy/"},{"id":647,"name":"Axel Springer Teaser Ad GmbH","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://www.adup-tech.com/privacy"},{"id":654,"name":"GRAPHINIUM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.graphinium.com/privacy/"},{"id":659,"name":"Research and Analysis of Media in Sweden AB","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www2.rampanel.com/privacy-policy/"},{"id":656,"name":"Think Clever Media","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.contentignite.com/privacy-policy/"},{"id":504,"name":"Alive & Kicking Global Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mcsaatchiplc.com/legal/privacy-cookies","deletedDate":"2020-07-27T00:00:00Z"},{"id":657,"name":"GP One GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.gsi-one.org/de/privacy-policy.html"},{"id":655,"name":"Sportradar AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sportradar.com/about-us/privacy/"},{"id":662,"name":"SoundCast","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://soundcast.fm/en/data-privacy"},{"id":665,"name":"Digital East GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.digitaleast.mobi/en/legal/privacy-policy/"},{"id":650,"name":"Telefonica Investigaci\u00f3n y Desarrollo S.A.U","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.cognitivemarketing.tid.es/"},{"id":666,"name":"BeOp","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://beop.io/privacy"},{"id":663,"name":"Mobsuccess","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.mobsuccess.com/en/privacy"},{"id":658,"name":"BLIINK SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://bliink.io/privacy-policy"},{"id":667,"name":"Liftoff Mobile, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://liftoff.io/privacy-policy/"},{"id":668,"name":"WhatRocks Inc. ","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.whatrocks.co/en/privacy-policy "},{"id":670,"name":"Timehop, Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.timehop.com/privacy"},{"id":674,"name":"Duration Media, LLC.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.durationmedia.net/privacy-policy"},{"id":675,"name":"Instreamatic inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://instreamatic.com/privacy-policy/"},{"id":676,"name":"BusinessClick","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.businessclick.com/documents/RegulaminProgramuBusinessClick-2019.pdf"},{"id":677,"name":"Intercept Interactive Inc. dba Undertone","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.undertone.com/privacy/"},{"id":660,"name":"Schibsted Norge AS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://static.vg.no/privacy/","deletedDate":"2019-09-16T00:00:00Z"},{"id":673,"name":"TTNET AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.programattik.com/en/privacy-policy.aspx"},{"id":664,"name":"adMarketplace, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.admarketplace.com/privacy-policy/"},{"id":671,"name":"Mediaforce LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://casino.mindthebet.co.uk/themes/mindthebetv2-casino/privacy.php"},{"id":561,"name":"AuDigent","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://audigent.com/platform-privacy-policy"},{"id":682,"name":"Radio Net Media Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.adtonos.com/service-privacy-policy/"},{"id":684,"name":"Blue Billywig BV","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.bluebillywig.com/privacy-statement/"},{"id":686,"name":"The MediaGrid Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.themediagrid.com/privacy-policy/"},{"id":685,"name":"Arkeero","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://arkeero.com/privacy-2/"},{"id":687,"name":"MISSENA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://missena.com/confidentialite/"},{"id":690,"name":"Go.pl sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://go.pl/polityka-prywatnosci/"},{"id":691,"name":"Lifesight Pte. Ltd.","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.lifesight.io/privacy-policy/"},{"id":697,"name":"ADWAYS SAS","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.adways.com/confidentialite/?lang=en"},{"id":681,"name":"MyTraffic","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mytraffic.io/en/privacy"},{"id":649,"name":"adality GmbH","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[1],"policyUrl":"https://adality.de/en/privacy/"},{"id":712,"name":"Inspired Mobile Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://byinspired.com/privacypolicy.pdf"},{"id":688,"name":"Effinity","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.effiliation.com/politique-de-confidentialite/"},{"id":702,"name":"Kwanko","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.kwanko.com/fr/rgpd/"},{"id":715,"name":"BidBerry SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.bidberrymedia.com/privacy-policy/"},{"id":713,"name":"Dataseat Ltd","purposeIds":[2,5],"legIntPurposeIds":[1,3,4],"featureIds":[],"policyUrl":"https://dataseat.com/privacy-policy"},{"id":716,"name":"OnAudience Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.onaudience.com/internet-advertising-privacy-policy"},{"id":708,"name":"Dugout Limited ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://dugout.com/privacy-policy"},{"id":717,"name":"Audience Network","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.en.audiencenetwork.pl/internet-advertising-privacy-policy"},{"id":718,"name":"AppConsent Xchange","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://appconsent.io/en/privacy-policy"},{"id":720,"name":"AAX LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://aax.media/privacy/"},{"id":678,"name":"Axonix LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://axonix.com/privacy-cookie-policy/"},{"id":719,"name":"Online Advertising Network Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.oan.pl/en/privacy-policy"},{"id":707,"name":"Dentsu Aegis Network Italia SpA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.dentsuaegisnetwork.com/it/it/policies/info-cookie"},{"id":721,"name":"Beaconspark Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1],"policyUrl":"https://www.engageya.com/privacy"},{"id":724,"name":"Between Exchange","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"https://en.betweenx.com/pdata.pdf"},{"id":728,"name":"Appier PTE Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.appier.com/privacy-policy/"},{"id":729,"name":"Cavai AS & UK ","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://cav.ai/privacy-policy/"},{"id":723,"name":"Adzymic Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.adzymic.co/privacy"},{"id":737,"name":"Monet Engine Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appmonet.com/privacy-policy/"},{"id":740,"name":"6Sense Insights, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://6sense.com/privacy-policy/"},{"id":744,"name":"Vidazoo Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[2],"policyUrl":"https://vidazoo.gitbook.io/vidazoo-legal/privacy-policy"},{"id":731,"name":"GeistM Technologies LTD","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.geistm.com/privacy"},{"id":741,"name":"Brand Advance Limited","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.wearebrandadvance.com/website-privacy-policy"},{"id":734,"name":"Cint AB","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.cint.com/participant-privacy-notice"},{"id":709,"name":"NC Audience Exchange, LLC (NewsIQ)","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.ncaudienceexchange.com/privacy/"},{"id":739,"name":"Blingby LLC","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://blingby.com/privacy"},{"id":732,"name":"Performax.cz, s.r.o.","purposeIds":[2,4,5],"legIntPurposeIds":[1,3],"featureIds":[2,3],"policyUrl":"https://reg.tiscali.cz/privacy-policy"},{"id":736,"name":"BidMachine Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://explorestack.com/privacy-policy/"},{"id":738,"name":"adbility media GmbH","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adbility-media.com/datenschutzerklaerung/"},{"id":742,"name":"Audiencerate LTD","purposeIds":[],"legIntPurposeIds":[1,2,5],"featureIds":[],"policyUrl":"https://www.audiencerate.com/privacy/"},{"id":743,"name":"MOVIads Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://moviads.pl/polityka-prywatnosci/"},{"id":746,"name":"Adxperience SAS","purposeIds":[2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://adxperience.com/privacy-policy/"},{"id":747,"name":"Kairion GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://kairion.de/datenschutzbestimmungen/"},{"id":748,"name":"AUDIOMOB LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.audiomob.io/privacy"},{"id":749,"name":"Good-Loop Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://doc.good-loop.com/policy/privacy-policy.html"},{"id":754,"name":"DistroScale, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.distroscale.com/privacy-policy/"},{"id":756,"name":"Fandom, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"https://www.fandom.com/privacy-policy"},{"id":758,"name":"GfK Netherlands B.V.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://gfkpanel.nl/privacy"},{"id":759,"name":"RevJet","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.revjet.com/privacy"},{"id":760,"name":"VEXPRO TECHNOLOGIES LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://onedash.com/privacy-policy.html"},{"id":761,"name":"Digiseg ApS","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://digiseg.io/privacy-center/"},{"id":763,"name":"Delidatax SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.delidatax.net/privacy.htm"},{"id":764,"name":"Lucidity","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://golucidity.com/privacy-policy/"},{"id":765,"name":"Grabit Interactive Media Inc dba KERV Interctive","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://kervit.com/privacy-policy/"},{"id":766,"name":"ADCELL | Firstlead GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.adcell.de/agb#sector_6"},{"id":768,"name":"Global Media & Entertainment Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://global.com/privacy-policy/"},{"id":770,"name":"MARKETPERF CORP","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.marketperf.com/assets/images/app/marketperf/pdf/privacy-policy.pdf"},{"id":773,"name":"360e-com Sp. z o.o.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.clickonometrics.com/optout/"},{"id":775,"name":"SelectMedia International LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selectmedia.asia/terms-and-privacy/"},{"id":778,"name":"Discover-Tech ltd","purposeIds":[2,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://discover-tech.io/dsp-privacy-policy/"},{"id":779,"name":"Adtarget Medya A.S.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtarget.com.tr/adtarget-privacy-policy-2020.pdf"},{"id":780,"name":"Aniview LTD","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.aniview.com/privacy-policy/"},{"id":781,"name":"FeedAd GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://feedad.com/privacy/"},{"id":784,"name":"Nubo LTD","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.recod3.com/privacypolicy.php"},{"id":786,"name":"TargetVideo GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.target-video.com/datenschutz/"},{"id":798,"name":"Adverticum cPlc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://adverticum.net/english/privacy-and-data-processing-information/"},{"id":803,"name":"Click Tech Limited","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[1],"policyUrl":"https://en.yeahmobi.com/html/privacypolicy/"}]} \ No newline at end of file +{"vendorListVersion":215,"lastUpdated":"2020-08-13T16:00:19Z","purposes":[{"id":1,"name":"Information storage and access","description":"The storage of information, or access to information that is already stored, on your device such as advertising identifiers, device identifiers, cookies, and similar technologies."},{"id":2,"name":"Personalisation","description":"The collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as on other websites or apps, over time. Typically, the content of the site or app is used to make inferences about your interests, which inform future selection of advertising and/or content."},{"id":3,"name":"Ad selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver advertisements for you, and to measure the delivery and effectiveness of such advertisements. This includes using previously collected information about your interests to select ads, processing data about what advertisements were shown, how often they were shown, when and where they were shown, and whether you took any action related to the advertisement, including for example clicking an ad or making a purchase. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise advertising and/or content for you in other contexts, such as websites or apps, over time."},{"id":4,"name":"Content selection, delivery, reporting","description":"The collection of information, and combination with previously collected information, to select and deliver content for you, and to measure the delivery and effectiveness of such content. This includes using previously collected information about your interests to select content, processing data about what content was shown, how often or how long it was shown, when and where it was shown, and whether the you took any action related to the content, including for example clicking on content. This does not include personalisation, which is the collection and processing of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, such as websites or apps, over time."},{"id":5,"name":"Measurement","description":"The collection of information about your use of the content, and combination with previously collected information, used to measure, understand, and report on your usage of the service. This does not include personalisation, the collection of information about your use of this service to subsequently personalise content and/or advertising for you in other contexts, i.e. on other service, such as websites or apps, over time."}],"features":[{"id":1,"name":"Matching Data to Offline Sources","description":"Combining data from offline sources that were initially collected in other contexts."},{"id":2,"name":"Linking Devices","description":"Allow processing of a user's data to connect such user across multiple devices."},{"id":3,"name":"Precise Geographic Location Data","description":"Allow processing of a user's precise geographic location data in support of a purpose for which that certain third party has consent."}],"vendors":[{"id":8,"name":"Emerse Sverige AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.emerse.com/privacy-policy/"},{"id":9,"name":"AdMaxim Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.admaxim.com/admaxim-privacy-policy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":12,"name":"BeeswaxIO Corporation","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beeswax.com/privacy/"},{"id":28,"name":"TripleLift, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://triplelift.com/privacy/"},{"id":27,"name":"ADventori SAS","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adventori.com/with-us/legal-notice/"},{"id":25,"name":"Verizon Media EMEA Limited","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.verizonmedia.com/policies/ie/en/verizonmedia/privacy/index.html"},{"id":26,"name":"Venatus Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.venatusmedia.com/privacy/"},{"id":1,"name":"Exponential Interactive, Inc d/b/a VDX.tv","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://vdx.tv/privacy/"},{"id":6,"name":"AdSpirit GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adspirit.de/privacy"},{"id":30,"name":"BidTheatre AB","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.bidtheatre.com/privacy-policy"},{"id":24,"name":"Epsilon","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.conversantmedia.eu/legal/privacy-policy"},{"id":29,"name":"Etarget SE","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.etarget.sk/privacy.php","deletedDate":"2020-06-01T00:00:00Z"},{"id":39,"name":"ADITION technologies AG","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.adition.com/datenschutz"},{"id":11,"name":"Quantcast International Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.quantcast.com/privacy/"},{"id":15,"name":"Adikteev","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adikteev.com/privacy-policy-eng/"},{"id":4,"name":"Roq.ad Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.roq.ad/privacy-policy"},{"id":7,"name":"Vibrant Media Limited","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vibrantmedia.com/en/privacy-policy/"},{"id":2,"name":"Captify Technologies Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.captify.co.uk/privacy-policy/"},{"id":37,"name":"NEURAL.ONE","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://web.neural.one/privacy-policy/"},{"id":13,"name":"Sovrn Holdings Inc","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sovrn.com/sovrn-privacy/"},{"id":34,"name":"NEORY GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.neory.com/privacy.html"},{"id":32,"name":"Xandr, Inc.","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.xandr.com/privacy/platform-privacy-policy/"},{"id":10,"name":"Index Exchange, Inc. ","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[2,3],"policyUrl":"https://www.indexexchange.com/privacy"},{"id":57,"name":"ADARA MEDIA UNLIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://adara.com/privacy-promise/"},{"id":63,"name":"Avocet Systems Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://avocet.io/privacy-portal"},{"id":51,"name":"xAd, Inc. dba GroundTruth","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.groundtruth.com/privacy-policy/"},{"id":49,"name":"TRADELAB","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://tradelab.com/en/privacy/"},{"id":45,"name":"Smart Adserver","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://smartadserver.com/end-user-privacy-policy/"},{"id":52,"name":"The Rubicon Project, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[3],"policyUrl":"http://www.rubiconproject.com/rubicon-project-yield-optimization-privacy-policy/"},{"id":71,"name":"Roku Advertising Services","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://docs.roku.com/published/userprivacypolicy/en/us"},{"id":79,"name":"MediaMath, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.mediamath.com/privacy-policy/"},{"id":91,"name":"Criteo SA","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.criteo.com/privacy/"},{"id":85,"name":"Crimtan Holdings Limited","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[1,3],"policyUrl":"https://crimtan.com/privacy/"},{"id":16,"name":"RTB House S.A.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.rtbhouse.com/privacy-center/services-privacy-policy/"},{"id":86,"name":"Scene Stealer Limited","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"http://scenestealer.tv/privacy-policy/"},{"id":94,"name":"Blis Media Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.blis.com/privacy/"},{"id":73,"name":"Simplifi Holdings Inc.","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2,3],"policyUrl":"https://simpli.fi/site-privacy-policy/"},{"id":33,"name":"ShareThis, Inc","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://sharethis.com/privacy/"},{"id":20,"name":"N Technologies Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://n.rich/privacy-notice"},{"id":55,"name":"Madison Logic, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.madisonlogic.com/privacy/"},{"id":53,"name":"Sirdata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.sirdata.com/privacy/"},{"id":69,"name":"OpenX","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.openx.com/legal/privacy-policy/"},{"id":98,"name":"GroupM UK Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.groupm.com/privacy-notice"},{"id":62,"name":"Justpremium BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://justpremium.com/privacy-policy/"},{"id":19,"name":"Intent Media, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://intentmedia.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":43,"name":"Vdopia DBA Chocolate Platform","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://chocolateplatform.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":36,"name":"RhythmOne DBA Unruly Group Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.rhythmone.com/privacy-policy"},{"id":80,"name":"Sharethrough, Inc","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://platform-cdn.sharethrough.com/privacy-policy"},{"id":81,"name":"PulsePoint, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pulsepoint.com/privacy-policy/website","deletedDate":"2020-07-06T00:00:00Z"},{"id":23,"name":"Amobee, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.amobee.com/trust/privacy-guidelines"},{"id":35,"name":"Purch Group, Inc.","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://www.purch.com/privacy-policy/","deletedDate":"2019-05-30T00:00:00Z"},{"id":3,"name":"affilinet","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.affili.net/de/footeritem/datenschutz","deletedDate":"2019-06-21T00:00:00Z"},{"id":74,"name":"Admotion SRL","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.admotion.com/policy/","deletedDate":"2019-07-24T00:00:00Z"},{"id":191,"name":"realzeit GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://realzeitmedia.com/privacy.html","deletedDate":"2019-04-29T00:00:00Z"},{"id":197,"name":"Switch Concepts Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.switchconcepts.com/privacy-policy","deletedDate":"2019-07-26T00:00:00Z"},{"id":390,"name":"Parsec Media Inc.","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,3],"policyUrl":"www.parsec.media/privacy-policy","deletedDate":"2019-06-27T00:00:00Z"},{"id":459,"name":"uppr GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://netzwerk.uppr.de/privacy-policy.do","deletedDate":"2019-06-17T00:00:00Z"},{"id":221,"name":"LEMO MEDIA GROUP LIMITED","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.lemomedia.com/terms.pdf","deletedDate":"2019-06-28T00:00:00Z"},{"id":478,"name":"RevLifter Ltd","purposeIds":[1],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.revlifter.com/privacy-policy","deletedDate":"2019-07-15T00:00:00Z"},{"id":500,"name":"Turbo","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.turboadv.com/white-rabbit-privacy-policy/","deletedDate":"2019-07-12T00:00:00Z"},{"id":68,"name":"Sizmek by Amazon","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.sizmek.com/privacy-policy/"},{"id":75,"name":"M32 Connect Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://m32.media/privacy-cookie-policy/"},{"id":17,"name":"Greenhouse Group BV (with its trademark LemonPI)","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.lemonpi.io/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":61,"name":"GumGum, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://gumgum.com/privacy-policy"},{"id":40,"name":"Active Agent (ADITION technologies AG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.active-agent.com/de/unternehmen/datenschutzerklaerung/"},{"id":76,"name":"PubMatic, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://pubmatic.com/privacy-policy/"},{"id":89,"name":"Tapad, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://www.tapad.com/eu-privacy-policy"},{"id":46,"name":"Skimbit Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://skimlinks.com/pages/privacy-policy"},{"id":66,"name":"adsquare GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adsquare.com/privacy"},{"id":105,"name":"Impression Desk Technologies Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://impressiondesk.com/privacy-policy/","deletedDate":"2019-08-06T00:00:00Z"},{"id":41,"name":"Adverline","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.adverline.com/privacy/"},{"id":82,"name":"Smaato, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.smaato.com/privacy/"},{"id":60,"name":"Rakuten Marketing LLC","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://rakutenadvertising.com/legal-notices/services-privacy-policy/"},{"id":70,"name":"Yieldlab AG","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[3],"policyUrl":"http://www.yieldlab.de/meta-navigation/datenschutz/"},{"id":50,"name":"Adform","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://site.adform.com/privacy-center/platform-privacy/product-and-services-privacy-policy/"},{"id":48,"name":"NetSuccess, s.r.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inres.sk/pp/"},{"id":100,"name":"Fifty Technology Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://fifty.io/privacy-policy.php"},{"id":21,"name":"The Trade Desk","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2,3],"policyUrl":"https://www.thetradedesk.com/general/privacy-policy"},{"id":110,"name":"Dynata LLC","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.opinionoutpost.co.uk/en-gb/policies/privacy"},{"id":42,"name":"Taboola Europe Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.taboola.com/privacy-policy"},{"id":112,"name":"Maytrics GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://maytrics.com/privacy.php","deletedDate":"2019-09-17T00:00:00Z"},{"id":77,"name":"comScore, Inc.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.scorecardresearch.com/privacy.aspx?newlanguage=1"},{"id":109,"name":"LoopMe Limited","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://loopme.com/privacy-policy/"},{"id":120,"name":"Eyeota Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.eyeota.com/privacy-center"},{"id":93,"name":"Adloox SA","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://adloox.com/disclaimer"},{"id":132,"name":"Teads ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.teads.com/privacy-policy/"},{"id":22,"name":"admetrics GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://admetrics.io/en/privacy_policy/"},{"id":102,"name":"Telaria SAS","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":108,"name":"Rich Audience Technologies SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://richaudience.com/privacy/"},{"id":18,"name":"Widespace AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.widespace.com/legal/privacy-policy-notice/"},{"id":122,"name":"Avid Media Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.avidglobalmedia.eu/privacy-policy.html"},{"id":97,"name":"LiveRamp, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.liveramp.com/service-privacy-policy/"},{"id":138,"name":"ConnectAd Realtime GmbH","purposeIds":[1,2],"legIntPurposeIds":[3,4],"featureIds":[],"policyUrl":"http://connectadrealtime.com/privacy/"},{"id":72,"name":"Nano Interactive GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.nanointeractive.com/privacy"},{"id":127,"name":"PIXIMEDIA SAS","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://piximedia.com/privacy/"},{"id":136,"name":"Str\u00f6er SSP GmbH (SSP)","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[2,3],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":111,"name":"Showheroes SE","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://showheroes.com/privacy/"},{"id":56,"name":"Confiant Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.confiant.com/privacy","deletedDate":"2020-05-18T00:00:00Z"},{"id":124,"name":"Teemo SA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://teemo.co/fr/confidentialite/"},{"id":154,"name":"YOC AG","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://yoc.com/privacy/"},{"id":38,"name":"Beemray Oy","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.beemray.com/privacy-policy/","deletedDate":"2020-06-19T00:00:00Z"},{"id":101,"name":"MiQ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://wearemiq.com/privacy-policy/"},{"id":149,"name":"ADman Interactive SLU","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://admanmedia.com/politica.html?setLng=es"},{"id":151,"name":"Admedo Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[3],"policyUrl":"https://www.admedo.com/privacy-policy","deletedDate":"2020-07-17T00:00:00Z"},{"id":153,"name":"MADVERTISE MEDIA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://madvertise.com/en/gdpr/"},{"id":159,"name":"Underdog Media LLC ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://underdogmedia.com/privacy-policy/"},{"id":157,"name":"Seedtag Advertising S.L","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.seedtag.com/en/privacy-policy/"},{"id":145,"name":"Snapsort Inc., operating as Sortable","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://help.sortable.com/help/privacy-policy"},{"id":131,"name":"ID5 Technology SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.id5.io/privacy"},{"id":158,"name":"Reveal Mobile, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://revealmobile.com/privacy"},{"id":147,"name":"Adacado Technologies Inc. (DBA Adacado)","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adacado.com/privacy-policy-april-25-2018/"},{"id":130,"name":"NextRoll, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.nextroll.com/privacy"},{"id":129,"name":"IPONWEB GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.iponweb.com/privacy-policy/"},{"id":128,"name":"BIDSWITCH GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bidswitch.com/privacy-policy/"},{"id":168,"name":"EASYmedia GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://login.rtbmarket.com/gdpr"},{"id":164,"name":"Outbrain UK Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.outbrain.com/legal/privacy#privacy-policy"},{"id":144,"name":"district m inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://districtm.net/en/page/platforms-data-and-privacy-policy/"},{"id":163,"name":"Bombora Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://bombora.com/privacy"},{"id":173,"name":"Yieldmo, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.yieldmo.com/privacy/"},{"id":88,"name":"TreSensa, Inc.","purposeIds":[1,3],"legIntPurposeIds":[2,5],"featureIds":[1],"policyUrl":"https://www.tresensa.com/eu-privacy"},{"id":78,"name":"Flashtalking, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.flashtalking.com/privacypolicy/"},{"id":59,"name":"Sift Media, Inc","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.sift.co/privacy"},{"id":114,"name":"Sublime","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://ayads.co/privacy.php"},{"id":175,"name":"FORTVISION","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://fortvision.com/POC/index.html","deletedDate":"2019-08-09T00:00:00Z"},{"id":133,"name":"digitalAudience","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://digitalaudience.io/legal/privacy-cookies/"},{"id":14,"name":"Adkernel LLC","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://adkernel.com/privacy-policy/"},{"id":180,"name":"Thirdpresence Oy","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"http://www.thirdpresence.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":183,"name":"EMX Digital LLC","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://emxdigital.com/privacy/"},{"id":58,"name":"33Across","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.33across.com/privacy-policy"},{"id":140,"name":"Platform161","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://platform161.com/cookie-and-privacy-policy/"},{"id":90,"name":"Teroa S.A.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.e-planning.net/en/privacy.html"},{"id":141,"name":"1020, Inc. dba Placecast and Ericsson Emodo","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.emodoinc.com/privacy-policy/"},{"id":142,"name":"Media.net Advertising FZ-LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.media.net/en/privacy-policy"},{"id":209,"name":"Delta Projects AB","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[3],"policyUrl":"https://deltaprojects.com/data-collection-policy"},{"id":195,"name":"advanced store GmbH","purposeIds":[2,3],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.advanced-store.com/de/datenschutz/"},{"id":190,"name":"video intelligence AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.vi.ai/privacy-policy/"},{"id":84,"name":"Semasio GmbH","purposeIds":[],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"http://www.semasio.com/privacy-policy/"},{"id":65,"name":"Location Sciences AI Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.locationsciences.ai/privacy-policy/"},{"id":210,"name":"Zemanta, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1],"policyUrl":"http://www.zemanta.com/legal/privacy"},{"id":200,"name":"Tapjoy, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.tapjoy.com/legal/#privacy-policy"},{"id":188,"name":"Sellpoints Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://retargeter.com/service-privacy-policy/","deletedDate":"2019-09-17T00:00:00Z"},{"id":217,"name":"2KDirect, Inc. (dba iPromote)","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.ipromote.com/privacy-policy/"},{"id":156,"name":"Centro, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.centro.net/privacy-policy/"},{"id":194,"name":"Rezonence Limited","purposeIds":[3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://rezonence.com/privacy-policy/"},{"id":226,"name":"Publicis Media GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.publicismedia.de/datenschutz/"},{"id":198,"name":"SYNC","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://redirect.sync.tv/privacy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":227,"name":"ORTEC B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.ortecadscience.com/privacy-policy/"},{"id":225,"name":"Ligatus GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.ligatus.com/en/privacy-policy","deletedDate":"2020-06-19T00:00:00Z"},{"id":205,"name":"Adssets AB","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"http://adssets.com/policy/"},{"id":179,"name":"Collective Europe Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.collectiveuk.com/privacy.html"},{"id":31,"name":"Ogury Ltd.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://www.ogury.com/privacy-policy/"},{"id":92,"name":"1plusX AG","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.1plusx.com/privacy-policy/"},{"id":155,"name":"AntVoice","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.antvoice.com/en/privacypolicy/"},{"id":115,"name":"smartclip Europe GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[2],"policyUrl":"https://privacy-portal.smartclip.net/"},{"id":126,"name":"DoubleVerify Inc.\u200b","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.doubleverify.com/privacy/"},{"id":193,"name":"Mediasmart Mobile S.L.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://mediasmart.io/privacy/"},{"id":245,"name":"IgnitionOne","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.ignitionone.com/privacy-policy/","deletedDate":"2020-06-30T00:00:00Z"},{"id":213,"name":"emetriq GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.emetriq.com/datenschutz/"},{"id":244,"name":"Temelio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://temelio.com/vie-privee"},{"id":224,"name":"adrule mobile GmbH","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.adrule.net/de/datenschutz/"},{"id":174,"name":"A Million Ads Ltd","purposeIds":[2],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://www.amillionads.com/privacy-policy"},{"id":192,"name":"remerge GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://remerge.io/privacy-policy.html"},{"id":232,"name":"Rockerbox, Inc","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"http://rockerbox.com/privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":256,"name":"Bounce Exchange, Inc","purposeIds":[1],"legIntPurposeIds":[2,4,5],"featureIds":[1,2],"policyUrl":"https://www.bouncex.com/privacy/"},{"id":234,"name":"ZBO Media","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zbo.media/mentions-legales/politique-de-confidentialite-service-publicitaire/"},{"id":246,"name":"Smartology Limited","purposeIds":[3],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://www.smartology.net/privacy-policy/"},{"id":241,"name":"OneTag Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.onetag.com/privacy/"},{"id":254,"name":"LiquidM Technology GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liquidm.com/privacy-policy/"},{"id":215,"name":"ARMIS SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://armis.tech/en/armis-personal-data-privacy-policy/"},{"id":167,"name":"Audiens S.r.l.","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.audiens.com/privacy"},{"id":240,"name":"7Hops.com Inc. (ZergNet)","purposeIds":[],"legIntPurposeIds":[1,4,5],"featureIds":[],"policyUrl":"https://zergnet.com/privacy"},{"id":235,"name":"Bucksense Inc","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.bucksense.com/platform-privacy-policy/"},{"id":185,"name":"Bidtellect, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.bidtellect.com/privacy-policy/"},{"id":258,"name":"Adello Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[3],"policyUrl":"https://www.adello.com/privacy-policy/"},{"id":169,"name":"RTK.IO, Inc","purposeIds":[1,4],"legIntPurposeIds":[2,3,5],"featureIds":[1,3],"policyUrl":"http://www.rtk.io/privacy.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":208,"name":"Spotad","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.spotad.co/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":211,"name":"AdTheorent, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://adtheorent.com/privacy-policy"},{"id":229,"name":"Digitize New Media Ltd","purposeIds":[2,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitize.ie/online-privacy","deletedDate":"2020-07-17T00:00:00Z"},{"id":273,"name":"Bannerflow AB","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.bannerflow.com/privacy "},{"id":104,"name":"Sonobi, Inc","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"http://sonobi.com/privacy-policy/"},{"id":162,"name":"Unruly Group Ltd","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://unruly.co/privacy/"},{"id":249,"name":"Spolecznosci Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.spolecznosci.pl/polityka-prywatnosci"},{"id":125,"name":"Research Now Group, Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.valuedopinions.co.uk/privacy","deletedDate":"2019-09-17T00:00:00Z"},{"id":170,"name":"Goodway Group, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://goodwaygroup.com/privacy-policy/"},{"id":160,"name":"Netsprint SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://netsprint.eu/privacy.html"},{"id":189,"name":"Intowow Innovation Ltd.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.intowow.com/privacy/","deletedDate":"2019-08-12T00:00:00Z"},{"id":279,"name":"Mirando GmbH & Co KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://wwwmirando.de/datenschutz/"},{"id":269,"name":"Sanoma Media Finland","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://sanoma.fi/tietoa-meista/tietosuoja/","deletedDate":"2019-08-07T00:00:00Z"},{"id":276,"name":"Viralize SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://viralize.com/privacy-policy"},{"id":87,"name":"Genius Sports Media Limited","purposeIds":[2,4],"legIntPurposeIds":[1,3,5],"featureIds":[2,3],"policyUrl":"https://www.geniussports.com/privacy-policy"},{"id":182,"name":"Collective, Inc. dba Visto","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vistohub.com/privacy-policy/","deletedDate":"2019-07-26T00:00:00Z"},{"id":255,"name":"Onnetwork Sp. z o.o.","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.onnetwork.tv/pp_services.php"},{"id":203,"name":"Revcontent, LLC","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://intercom.help/revcontent2/en/articles/2290675-revcontent-s-privacy-policy"},{"id":260,"name":"RockYou, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,5],"featureIds":[3],"policyUrl":"https://rockyou.com/privacy-policy/","deletedDate":"2019-08-09T00:00:00Z"},{"id":237,"name":"LKQD, a division of Nexstar Digital, LLC.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.lkqd.com/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":274,"name":"Golden Bees","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.goldenbees.fr/en/privacy-charter/"},{"id":280,"name":"Spot.IM LTD","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.spot.im/privacy/"},{"id":239,"name":"Triton Digital Canada Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.tritondigital.com/privacy-policies"},{"id":177,"name":"plista GmbH","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.plista.com/about/privacy/"},{"id":201,"name":"TimeOne","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://privacy.timeonegroup.com/en/","deletedDate":"2020-05-15T00:00:00Z"},{"id":150,"name":"Inskin Media LTD","purposeIds":[2,3,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"http://www.inskinmedia.com/privacy-policy.html"},{"id":252,"name":"Jaduda GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.jadudamobile.com/datenschutzerklaerung/"},{"id":248,"name":"Converge-Digital","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://converge-digital.com/privacy-policy/"},{"id":161,"name":"Smadex SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://smadex.com/end-user-privacy-policy/"},{"id":285,"name":"Comcast International France SAS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.freewheel.com/privacy-policy"},{"id":228,"name":"McCann Discipline LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.primis.tech/privacy-policy/"},{"id":299,"name":"AdClear GmbH","purposeIds":[1,5],"legIntPurposeIds":[2,3,4],"featureIds":[1,2],"policyUrl":"https://www.adclear.de/datenschutzerklaerung/"},{"id":277,"name":"Codewise VL Sp. z o.o. Sp. k","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://voluumdsp.com/end-user-privacy-policy/"},{"id":259,"name":"ADYOULIKE SA","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.adyoulike.com/privacy_policy.php"},{"id":272,"name":"A.Mob","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.we-are-adot.com/privacy-policy/"},{"id":230,"name":"Steel House, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://steelhouse.com/privacy-policy/"},{"id":253,"name":"Improve Digital BV","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.improvedigital.com/platform-privacy-policy"},{"id":304,"name":"On Device Research Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://s.on-device.com/privacyPolicy"},{"id":314,"name":"Keymantics","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.keymantics.com/assets/privacy-policy.pdf"},{"id":257,"name":"R-TARGET","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"http://www.r-target.com/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":317,"name":"mainADV Srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.mainad.com/privacy-policy/"},{"id":278,"name":"Integral Ad Science, Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://integralads.com/privacy-policy/"},{"id":291,"name":"Qwertize","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.qwertize.com/en/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":295,"name":"Sojern, Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.sojern.com/privacy/product-privacy-policy/"},{"id":315,"name":"Celtra, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.celtra.com/privacy-policy/"},{"id":165,"name":"SpotX, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1],"policyUrl":"https://www.spotx.tv/privacy-policy/"},{"id":47,"name":"ADMAN - Phaistos Networks, S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.adman.gr/privacy"},{"id":134,"name":"SMARTSTREAM.TV GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2],"policyUrl":"https://www.smartstream.tv/en/productprivacy"},{"id":325,"name":"Knorex","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.knorex.com/privacy"},{"id":316,"name":"Gamned","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.gamned.com/privacy-policy/"},{"id":318,"name":"Accorp Sp. z o.o.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"http://www.instytut-pollster.pl/privacy-policy/"},{"id":199,"name":"ADUX","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adux.com/donnees-personelles/"},{"id":236,"name":"PowerLinks Media Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[3],"policyUrl":"https://www.powerlinks.com/privacy-policy/"},{"id":294,"name":"Jivox Corporation","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.jivox.com/privacy"},{"id":143,"name":"Connatix Native Exchange Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://connatix.com/privacy-policy/"},{"id":297,"name":"Polar Mobile Group Inc.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://privacy.polar.me"},{"id":319,"name":"Clipcentric, Inc.","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://clipcentric.com/privacy.bhtml"},{"id":290,"name":"Readpeak Oy","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://readpeak.com/privacy-policy/"},{"id":323,"name":"DAZN Media Services Limited","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://www.goal.com/en-gb/legal/privacy-policy"},{"id":119,"name":"Fusio by S4M","purposeIds":[1,2,5],"legIntPurposeIds":[3],"featureIds":[1,3],"policyUrl":"http://www.s4m.io/privacy-policy/"},{"id":302,"name":"Mobile Professionals BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mobpro.com/privacy.html"},{"id":212,"name":"usemax advertisement (Emego GmbH)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.usemax.de/?l=privacy"},{"id":264,"name":"Adobe Advertising Cloud","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.adobe.com/privacy/experience-cloud.html"},{"id":44,"name":"The ADEX GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://theadex.com/privacy-opt-out/"},{"id":282,"name":"Welect GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.welect.de/datenschutz"},{"id":238,"name":"StackAdapt","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.stackadapt.com/privacy"},{"id":284,"name":"WEBORAMA","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://weborama.com/privacy_en/"},{"id":148,"name":"Liveintent Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://liveintent.com/services-privacy-policy/"},{"id":64,"name":"DigiTrust / IAB Tech Lab","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.digitru.st/privacy-policy/"},{"id":301,"name":"zeotap GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://zeotap.com/privacy_policy"},{"id":275,"name":"TabMo SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://static.tabmo.io.s3.amazonaws.com/privacy-policy/index.html"},{"id":310,"name":"Adevinta Spain S.L.U.","purposeIds":[],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"https://www.adevinta.com/about/privacy/"},{"id":139,"name":"Permodo GmbH","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://permodo.com/de/privacy.html"},{"id":326,"name":"AdTiming Technology Company Limited","purposeIds":[3,5],"legIntPurposeIds":[1,2,4],"featureIds":[],"policyUrl":"http://www.adtiming.com/en/privacypolicy.html"},{"id":262,"name":"Fyber ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.fyber.com/legal/privacy-policy/"},{"id":331,"name":"ad6media","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.ad6media.fr/privacy"},{"id":345,"name":"The Kantar Group Limited","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.kantar.com/cookies-policies"},{"id":308,"name":"Rockabox Media Ltd","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[],"policyUrl":"http://scoota.com/privacy-policy"},{"id":270,"name":"Marfeel Solutions, SL","purposeIds":[],"legIntPurposeIds":[2],"featureIds":[],"policyUrl":"https://www.marfeel.com/privacy-policy/"},{"id":333,"name":"InMobi Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":202,"name":"Telaria, Inc","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://telaria.com/privacy-policy/"},{"id":328,"name":"Gemius SA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.gemius.com/cookie-policy.html"},{"id":281,"name":"Wizaly","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.wizaly.com/terms-of-use#privacy-policy"},{"id":354,"name":"Apester Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://apester.com/privacy-policy/"},{"id":320,"name":"Adelphic LLC","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://adelphic.com/platform/privacy/"},{"id":359,"name":"AerServ LLC","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.inmobi.com/privacy-policy-for-eea"},{"id":265,"name":"Instinctive, Inc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://instinctive.io/privacy"},{"id":349,"name":"Optomaton UG","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://optomaton.com/privacy.html"},{"id":288,"name":"Video Media Groep B.V.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://www.videomediagroup.com/wp-content/uploads/2016/01/Privacy-policy-VMG.pdf","deletedDate":"2019-09-17T00:00:00Z"},{"id":266,"name":"Digilant Spain, SLU","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.digilant.com/es/politica-privacidad/"},{"id":339,"name":"Vuble","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.vuble.tv/us/privacy","deletedDate":"2019-08-26T00:00:00Z"},{"id":303,"name":"Orion Semantics","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://static.orion-semantics.com/privacy.html"},{"id":261,"name":"Signal Digital Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.signal.co/privacy-policy/"},{"id":83,"name":"Visarity Technologies GmbH","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://primo.design/docs/PrivacyPolicyPrimo.html"},{"id":343,"name":"DIGITEKA Technologies","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.ultimedia.com/POLICY.html"},{"id":330,"name":"Linicom","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://www.linicom.com/privacy/","deletedDate":"2020-06-08T00:00:00Z"},{"id":231,"name":"AcuityAds Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.acuityads.com/corporate-privacy-policy.html"},{"id":216,"name":"Mindlytix SAS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://mindlytix.com/privacy/"},{"id":311,"name":"Mobfox US LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobfox.com/privacy-policy/"},{"id":358,"name":"MGID Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mgid.com/privacy-policy"},{"id":152,"name":"Meetrics GmbH","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.meetrics.com/en/data-privacy/"},{"id":251,"name":"Yieldlove GmbH","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"http://www.yieldlove.com/cookie-policy"},{"id":344,"name":"My6sense Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[2,4],"featureIds":[],"policyUrl":"https://my6sense.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":347,"name":"Ezoic Inc.","purposeIds":[2,4,5],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.ezoic.com/terms/"},{"id":218,"name":"Bigabid Media ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://www.bigabid.com/privacy-policy"},{"id":350,"name":"Free Stream Media Corp. dba Samba TV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":351,"name":"Samba TV UK Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://samba.tv/legal/privacy-policy/"},{"id":341,"name":"Somo Audience Corp","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[1,2,3],"policyUrl":"https://somoaudience.com/legal/","deletedDate":"2020-07-06T00:00:00Z"},{"id":380,"name":"Vidoomy Media SL","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"http://vidoomy.com/privacy-policy.html"},{"id":378,"name":"communicationAds GmbH & Co. KG","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.communicationads.net/aboutus/privacy/"},{"id":369,"name":"Getintent USA, inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://getintent.com/privacy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":184,"name":"mediarithmics SAS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mediarithmics.com/en-us/content/privacy-policy"},{"id":368,"name":"VECTAURY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.vectaury.io/en/personal-data"},{"id":373,"name":"Nielsen Marketing Cloud","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"http://www.nielsen.com/us/en/privacy-statement/exelate-privacy-policy.html"},{"id":214,"name":"Digital Control GmbH & Co. KG","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://advolution.de/privacy.php","deletedDate":"2020-05-06T00:00:00Z"},{"id":388,"name":"numberly","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://numberly.com/en/privacy/"},{"id":250,"name":"Qriously Ltd","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.brandwatch.com/legal/qriously-privacy-notice/"},{"id":223,"name":"Audience Trading Platform Ltd.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://atp.io/privacy-policy"},{"id":387,"name":"Triapodi Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appreciate.mobi/page.html#/end-user-privacy-policy"},{"id":312,"name":"Exactag GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.exactag.com/en/data-privacy/"},{"id":178,"name":"Hybrid Theory","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://hybridtheory.com/privacy-policy/"},{"id":377,"name":"AddApptr GmbH","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.addapptr.com/data-privacy"},{"id":382,"name":"The Reach Group GmbH","purposeIds":[1,2,4,5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://trg.de/en/privacy-statement/"},{"id":206,"name":"Hybrid Adtech GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://hybrid.ai/data_protection_policy"},{"id":403,"name":"Mobusi Mobile Advertising S.L.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mobusi.com/privacy.en.html","deletedDate":"2020-07-17T00:00:00Z"},{"id":385,"name":"Oracle Data Cloud","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://www.oracle.com/legal/privacy/marketing-cloud-data-cloud-privacy-policy.html"},{"id":404,"name":"Duplo Media AS","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.easy-ads.com/privacypolicy.htm"},{"id":242,"name":"twiago GmbH","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.twiago.com/datenschutz/"},{"id":376,"name":"Pocketmath Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.pocketmath.com/privacy-policy"},{"id":402,"name":"Effiliation","purposeIds":[2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://inter.effiliation.com/politique-confidentialite.html"},{"id":413,"name":"Eulerian Technologies","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.eulerian.com/en/privacy/"},{"id":400,"name":"Whenever Media Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.whenevermedia.com/privacy-policy","deletedDate":"2019-07-29T00:00:00Z"},{"id":171,"name":"Webedia","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webedia-group.com/site/privacy-policy","deletedDate":"2020-07-01T00:00:00Z"},{"id":398,"name":"Yormedia Solutions Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.yormedia.com/privacy-and-cookies-notice/","deletedDate":"2019-08-06T00:00:00Z"},{"id":415,"name":"Seenthis AB","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://seenthis.co/privacy-notice-2018-04-18.pdf"},{"id":263,"name":"Nativo, Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.nativo.com/interest-based-ads"},{"id":329,"name":"Browsi Mobile Ltd","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://gobrowsi.com/browsi-privacy-policy/"},{"id":389,"name":"Bidmanagement GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adspert.net/en/privacy/","deletedDate":"2020-07-01T00:00:00Z"},{"id":337,"name":"SheMedia, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shemedia.com/ad-services-privacy-policy"},{"id":422,"name":"Brand Metrics Sweden AB","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://collector.brandmetrics.com/brandmetrics_privacypolicy.pdf"},{"id":421,"name":"LeftsnRight, Inc. dba LIQWID","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://liqwid.solutions/privacy-policy","deletedDate":"2020-06-30T00:00:00Z"},{"id":426,"name":"TradeTracker","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[2],"policyUrl":"https://tradetracker.com/privacy-policy/","deletedDate":"2019-08-21T00:00:00Z"},{"id":394,"name":"AudienceProject Aps","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://privacy.audienceproject.com"},{"id":287,"name":"Avazu Inc.","purposeIds":[],"legIntPurposeIds":[1,3,4],"featureIds":[3],"policyUrl":"http://avazuinc.com/opt-out/","deletedDate":"2020-08-03T00:00:00Z"},{"id":243,"name":"Cloud Technologies S.A.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cloudtechnologies.pl/en/internet-advertising-privacy-policy"},{"id":113,"name":"iotec global Ltd.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.iotecglobal.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":338,"name":"dunnhumby Germany GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.sociomantic.com/privacy/en/","deletedDate":"2020-07-17T00:00:00Z"},{"id":405,"name":"IgnitionAi Ltd","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[2],"policyUrl":"https://www.isitelab.io/default.aspx","deletedDate":"2020-07-03T00:00:00Z"},{"id":416,"name":"Commanders Act","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.commandersact.com/en/privacy/"},{"id":434,"name":"DynAdmic","purposeIds":[1,3],"legIntPurposeIds":[2,4],"featureIds":[1,3],"policyUrl":"http://eu.dynadmic.com/privacy-policy/"},{"id":435,"name":"SINGLESPOT SAS ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.singlespot.com/privacy_policy?locale=fr"},{"id":409,"name":"Arrivalist Co.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[1,2],"policyUrl":"https://www.arrivalist.com/privacy"},{"id":321,"name":"Ziff Davis LLC","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.ziffdavis.com/privacy-policy"},{"id":436,"name":"INVIBES GROUP","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[1,2,3],"policyUrl":"http://www.invibes.com/terms"},{"id":442,"name":"R-Advertising","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-20T00:00:00Z"},{"id":362,"name":"Myntelligence S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://myntelligence.com/privacy-page/"},{"id":418,"name":"PROXISTORE","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://www.proxistore.com/common/en/cgv"},{"id":449,"name":"Mobile Journey B.V.","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://mobilejourney.com/Privacy-Policy","deletedDate":"2019-09-05T00:00:00Z"},{"id":443,"name":"Tradedoubler AB","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[2],"policyUrl":"https://www.tradedoubler.com/en/privacy-policy/","deletedDate":"2019-08-13T00:00:00Z"},{"id":429,"name":"Signals","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://signalsdata.com/platform-cookie-policy/"},{"id":335,"name":"Beachfront Media LLC","purposeIds":[],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"http://beachfront.com/privacy-policy/"},{"id":407,"name":"Publishers Internationale Pty Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pi-rate.com.au/privacy.html","deletedDate":"2019-11-08T00:00:00Z"},{"id":427,"name":"Proxi.cloud Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://proxi.cloud/info/privacy-policy/"},{"id":374,"name":"Bmind a Sales Maker Company, S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.bmind.es/legal-notice/"},{"id":438,"name":"INVIDI technologies AB","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"http://www.invidi.com/wp-content/uploads/2020/02/ad-tech-services-privacy-policy.pdf"},{"id":450,"name":"Neodata Group srl","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.neodatagroup.com/en/security-policy"},{"id":452,"name":"Innovid Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.innovid.com/privacy-policy"},{"id":444,"name":"Playbuzz Ltd (aka EX.CO)","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://ex.co/privacy-policy/"},{"id":412,"name":"Cxense ASA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.cxense.com/about-us/privacy-policy"},{"id":454,"name":"Adimo","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://adimo.co/privacy-policy/","deletedDate":"2019-09-12T00:00:00Z"},{"id":455,"name":"GDMServices, Inc. d/b/a FiksuDSP","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://fiksu.com/privacy-policy/"},{"id":298,"name":"Cuebiq Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.cuebiq.com/privacypolicy/","deletedDate":"2019-08-30T00:00:00Z"},{"id":423,"name":"travel audience GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://travelaudience.com/product-privacy-policy/"},{"id":397,"name":"Demandbase, Inc. ","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.demandbase.com/privacy-policy/"},{"id":381,"name":"Solocal","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://frontend.adhslx.com/privacy.html?"},{"id":425,"name":"ADRINO Sp. z o.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.adrino.pl/ciasteczkowa-polityka/","deletedDate":"2019-09-05T00:00:00Z"},{"id":365,"name":"Forensiq LLC","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1,3],"policyUrl":"https://impact.com/privacy-policy/"},{"id":447,"name":"Adludio Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.adludio.com/privacy-policy/"},{"id":410,"name":"Adtelligent Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtelligent.com/privacy-policy/"},{"id":137,"name":"Str\u00f6er SSP GmbH (DSP)","purposeIds":[],"legIntPurposeIds":[1,2,3],"featureIds":[],"policyUrl":"https://www.stroeer.de/fileadmin/de/Konvergenz_und_Konzepte/Daten_und_Technologien/Stroeer_SSP/Downloads/Datenschutz_Stroeer_SSP.pdf"},{"id":395,"name":"PREX Programmatic Exchange GmbH&Co KG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[],"policyUrl":"http://www.programmatic-exchange.com/privacy","deletedDate":"2020-07-03T00:00:00Z"},{"id":462,"name":"Bidstack Limited","purposeIds":[1,2,5],"legIntPurposeIds":[3,4],"featureIds":[2],"policyUrl":"https://www.bidstack.com/privacy-policy/"},{"id":466,"name":"TACTIC\u2122 Real-Time Marketing AS","purposeIds":[],"legIntPurposeIds":[4,5],"featureIds":[],"policyUrl":"https://tacticrealtime.com/privacy/"},{"id":340,"name":"Yieldr UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.yieldr.com/privacy"},{"id":336,"name":"Telecoming S.A.","purposeIds":[3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.telecoming.com/privacy-policy/"},{"id":430,"name":"Ad Unity Ltd","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"http://www.adunity.com/privacy-policy.html","deletedDate":"2019-08-13T00:00:00Z"},{"id":346,"name":"Cybba, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://cybba.com/about/legal/data-processing-agreement/","deletedDate":"2020-08-03T00:00:00Z"},{"id":469,"name":"Zeta Global","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://zetaglobal.com/privacy-policy/"},{"id":440,"name":"DEFINE MEDIA GMBH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.definemedia.de/datenschutz-conative/"},{"id":375,"name":"Affle International","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://affle.com/privacy-policy "},{"id":196,"name":"AdElement Media Solutions Pvt Ltd","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"http://adelement.com/privacy-policy.html"},{"id":268,"name":"Social Tokens Ltd. ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://woobi.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":475,"name":"TAPTAP Digital SL","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1,2,3],"policyUrl":"http://www.taptapnetworks.com/privacy_policy/"},{"id":474,"name":"hbfsTech","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.hbfstech.com/fr/privacy.html"},{"id":448,"name":"Targetspot Belgium SPRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://marketing.targetspot.com/Targetspot/Legal/TargetSpot%20Privacy%20Policy%20-%20June%202018.pdf"},{"id":428,"name":"Internet BillBoard a.s.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"http://www.ibillboard.com/en/privacy-information/"},{"id":461,"name":"B2B Media Group EMEA GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selfcampaign.com/static/privacy","deletedDate":"2019-08-14T00:00:00Z"},{"id":476,"name":"HIRO Media Ltd","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"http://hiro-media.com/privacy.php"},{"id":480,"name":"pilotx.tv","purposeIds":[2,3],"legIntPurposeIds":[1,4,5],"featureIds":[1,2,3],"policyUrl":"https://pilotx.tv/privacy/"},{"id":366,"name":"CerebroAd.com s.r.o.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.cerebroad.com/privacy-policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":392,"name":"Str\u00f6er Mobile Performance GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[3],"policyUrl":"https://stroeermobileperformance.com/?dl=privacy"},{"id":357,"name":"Totaljobs Group Ltd ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.totaljobs.com/privacy-policy"},{"id":486,"name":"Madington","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://delivered-by-madington.com/dat-privacy-policy/"},{"id":468,"name":"NeuStar, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,5],"featureIds":[1,2],"policyUrl":"https://www.home.neustar/privacy"},{"id":458,"name":"AdColony, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1],"policyUrl":"adcolony.com/privacy-policy/"},{"id":489,"name":"YellowHammer Media Group","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.yhmg.com/privacy-policy/","deletedDate":"2019-11-27T00:00:00Z"},{"id":293,"name":"SpringServe, LLC","purposeIds":[],"legIntPurposeIds":[1,3],"featureIds":[],"policyUrl":"https://springserve.com/privacy-policy/"},{"id":484,"name":"STRIATUM SAS","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://adledge.com/data-privacy/"},{"id":493,"name":"Carbon (AI) Limited","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"https://carbonrmp.com/privacy.html"},{"id":495,"name":"Arcspire Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://public.arcspire.io/privacy.pdf"},{"id":496,"name":"Automattic Inc.","purposeIds":[1,2,3,4],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://en.blog.wordpress.com/2017/12/04/updated-privacy-policy/"},{"id":424,"name":"KUPONA GmbH","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.kupona.de/dsgvo/"},{"id":408,"name":"Fidelity Media","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://fidelity-media.com/privacy-policy/"},{"id":473,"name":"Sub2 Technologies Ltd","purposeIds":[3,4,5],"legIntPurposeIds":[1,2],"featureIds":[],"policyUrl":"http://www.sub2tech.com/privacy-policy/"},{"id":467,"name":"Haensel AMS GmbH","purposeIds":[3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://haensel-ams.com/data-privacy/"},{"id":490,"name":"PLAYGROUND XYZ EMEA LTD","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://playground.xyz/privacy"},{"id":464,"name":"Oracle AddThis","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[2],"policyUrl":"http://www.addthis.com/privacy/privacy-policy/","deletedDate":"2020-02-12T00:00:00Z"},{"id":491,"name":"Triboo Data Analytics","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shinystat.com/it/informativa_privacy_generale.html"},{"id":499,"name":"PurposeLab, LLC","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://purposelab.com/privacy/","deletedDate":"2019-10-02T00:00:00Z"},{"id":502,"name":"NEXD","purposeIds":[5],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://nexd.com/privacy-policy"},{"id":465,"name":"Schibsted Product and Tech UK","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.schibsted.com/","deletedDate":"2019-07-26T00:00:00Z"},{"id":497,"name":"Little Big Data sp.z.o.o.","purposeIds":[1,2,4],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://dtxngr.com/legal/"},{"id":492,"name":"LotaData, Inc.","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[1],"policyUrl":"https://lotadata.com/privacy_policy","deletedDate":"2019-10-02T00:00:00Z"},{"id":512,"name":"PubNative GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://pubnative.net/privacy-notice/"},{"id":471,"name":"FlexOffers.com, LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.flexoffers.com/privacy-policy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":494,"name":"Cablato Limited","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://cablato.com/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":516,"name":"Pexi B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://pexi.nl/privacy-policy/"},{"id":507,"name":"AdsWizz Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://www.adswizz.com/our-privacy-policy/"},{"id":482,"name":"UberMedia, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ubermedia.com/summary-of-privacy-policy/"},{"id":505,"name":"Shopalyst Inc","purposeIds":[1,2],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.shortlyst.com/eu/privacy_terms.html"},{"id":517,"name":"SunMedia ","purposeIds":[1,2],"legIntPurposeIds":[3],"featureIds":[2],"policyUrl":"https://www.sunmedia.tv/en/cookies"},{"id":518,"name":"Accelerize Inc.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[2,3],"policyUrl":"https://getcake.com/privacy-policy/","deletedDate":"2020-07-17T00:00:00Z"},{"id":511,"name":"Admixer EU GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://admixer.com/privacy/"},{"id":479,"name":"INFINIA MOBILE S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.infiniamobile.com/privacy_policy"},{"id":513,"name":"Shopstyle","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.shopstyle.co.uk/privacy","deletedDate":"2019-10-02T00:00:00Z"},{"id":509,"name":"ATG Ad Tech Group GmbH","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://ad-tech-group.com/privacy-policy/"},{"id":521,"name":"netzeffekt GmbH","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://www.netzeffekt.de/en/imprint"},{"id":487,"name":"nugg.ad GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1],"policyUrl":"https://www.nugg.ad/en/privacy/general-information.html","deletedDate":"2019-10-03T00:00:00Z"},{"id":515,"name":"ZighZag","purposeIds":[1,3],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://zighzag.com/privacy"},{"id":520,"name":"ChannelSight ","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.channelsight.com/privacypolicy/"},{"id":524,"name":"The Ozone Project Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://ozoneproject.com/privacy-policy"},{"id":529,"name":"Fidzup","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.fidzup.com/en/privacy/","deletedDate":"2019-11-18T00:00:00Z"},{"id":528,"name":"Kayzen","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://kayzen.io/data-privacy-policy"},{"id":527,"name":"Jampp LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://jampp.com/privacy.html"},{"id":506,"name":"salesforce.com, inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.salesforce.com/company/privacy/"},{"id":534,"name":"SmartyAds Inc.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://smartyads.com/privacy-policy"},{"id":535,"name":"INNITY","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.innity.com/privacy-policy.php"},{"id":514,"name":"Uprival LLC","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://uprival.com/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":522,"name":"Tealium Inc.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://tealium.com/privacy-policy/"},{"id":530,"name":"Near Pte Ltd","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://near.co/privacy"},{"id":539,"name":"AdDefend GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.addefend.com/en/privacy-policy/"},{"id":501,"name":"Alliance Gravity Data Media","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.alliancegravity.com/politiquedeprotectiondesdonneespersonnelles"},{"id":519,"name":"Chargeads","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.chargeplatform.com/privacy"},{"id":523,"name":"X-Mode Social, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://xmode.io/privacy-policy.html"},{"id":537,"name":"RUN, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.runads.com/privacy-policy"},{"id":531,"name":"Smartclip Hispania SL","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://rgpd-smartclip.com/"},{"id":536,"name":"GlobalWebIndex","purposeIds":[1],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"http://legal.trendstream.net/non-panellist_privacy_policy"},{"id":542,"name":"Densou Trading Desk ApS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://densou.dk/Policy.html","deletedDate":"2020-01-21T00:00:00Z"},{"id":525,"name":"PUB OCEAN LIMITED","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://rta.pubocean.com/privacy-policy/","deletedDate":"2019-10-03T00:00:00Z"},{"id":544,"name":"Kochava Inc.","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1,2],"policyUrl":"https://www.kochava.com/support-privacy/"},{"id":543,"name":"PaperG, Inc. dba Thunder Industries","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.makethunder.com/privacy"},{"id":334,"name":"Cydersoft","purposeIds":[],"legIntPurposeIds":[1,2,3,4],"featureIds":[2,3],"policyUrl":"http://www.videmob.com/privacy.html"},{"id":551,"name":"Illuma Technology Limited","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.weareilluma.com/endddd","deletedDate":"2019-11-14T00:00:00Z"},{"id":540,"name":"Tunnl BV","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://tunnl.com/privacy.html","deletedDate":"2019-12-20T00:00:00Z"},{"id":547,"name":"Video Reach","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.videoreach.de/about/privacy-policy/","deletedDate":"2020-01-21T00:00:00Z"},{"id":546,"name":"Smart Traffik","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://okube-attribution.com/politique-de-confidentialite/"},{"id":541,"name":"DeepIntent, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://www.deepintent.com/privacypolicy"},{"id":545,"name":"Reignn Platform Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://reignn.com/user-privacy-policy"},{"id":439,"name":"Bit Q Holdings Limited","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.rippll.com/privacy"},{"id":553,"name":"Adhese","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://adhese.com/privacy-and-cookie-policy"},{"id":556,"name":"adhood.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://v3.adhood.com/en/site/politikavekurallar/gizlilik.php?lang=en"},{"id":550,"name":"Happydemics","purposeIds":[5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.iubenda.com/privacy-policy/69056167/full-legal"},{"id":560,"name":"Leiki Ltd.","purposeIds":[1,2,3],"legIntPurposeIds":[4],"featureIds":[],"policyUrl":"http://www.leiki.com/privacy","deletedDate":"2020-01-07T00:00:00Z"},{"id":554,"name":"RMSi Radio Marketing Service interactive GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.rms.de/datenschutz/"},{"id":498,"name":"Mediakeys Platform","purposeIds":[1],"legIntPurposeIds":[3],"featureIds":[3],"policyUrl":"https://drbanner.com/privacypolicy_en/"},{"id":565,"name":"Adobe Audience Manager","purposeIds":[1,2,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adobe.com/privacy/policy.html"},{"id":118,"name":"Drawbridge, Inc.","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.drawbridge.com/privacy/","deletedDate":"2020-03-06T00:00:00Z"},{"id":572,"name":"CHEQ AI TECHNOLOGIES LTD.","purposeIds":[1],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"http://www.cheq.ai/privacy"},{"id":571,"name":"ViewPay","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://viewpay.tv/mentions-legales/"},{"id":568,"name":"Jointag S.r.l.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.jointag.com/privacy/kariboo/publisher/third/"},{"id":570,"name":"Czech Publisher Exchange z.s.p.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.cpex.cz/pro-uzivatele/ochrana-soukromi/"},{"id":559,"name":"Otto (GmbH & Co KG)","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2],"policyUrl":"https://www.otto.de/shoppages/service/datenschutz"},{"id":548,"name":"LBC France","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.leboncoin.fr/dc/cookies","deletedDate":"2020-04-23T00:00:00Z"},{"id":569,"name":"Kairos Fire","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.kairosfire.com/privacy"},{"id":577,"name":"Neustar on behalf of The Procter & Gamble Company","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.pg.com/privacy/english/privacy_statement.shtml"},{"id":590,"name":"Sourcepoint Technologies, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.sourcepoint.com/privacy-policy"},{"id":587,"name":"Localsensor B.V.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[],"policyUrl":"https://www.localsensor.com/privacy.html"},{"id":578,"name":"MAIRDUMONT NETLETIX GmbH&Co. KG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://mairdumont-netletix.com/datenschutz"},{"id":580,"name":"Goldbach Group AG","purposeIds":[],"legIntPurposeIds":[1,2,3,5],"featureIds":[1,2,3],"policyUrl":"https://goldbach.com/ch/de/datenschutz"},{"id":593,"name":"Programatica de publicidad S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://datmean.com/politica-privacidad/"},{"id":574,"name":"Realeyes OU","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://realview.realeyesit.com/privacy"},{"id":581,"name":"Mobilewalla, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[1,2,3],"policyUrl":"https://www.mobilewalla.com/business-services-privacy-policy"},{"id":598,"name":"audio content & control GmbH","purposeIds":[1],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://www.audio-cc.com/audiocc_privacy_policy.pdf"},{"id":596,"name":"InsurAds Technologies SA.","purposeIds":[3],"legIntPurposeIds":[5],"featureIds":[3],"policyUrl":"https://www.insurads.com/privacy.html"},{"id":576,"name":"StartApp Inc.","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[3],"policyUrl":"https://www.startapp.com/policy/privacy-policy/","deletedDate":"2020-04-23T00:00:00Z"},{"id":592,"name":"Colpirio.com","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy-policy.colpirio.com/en/","deletedDate":"2020-03-18T00:00:00Z"},{"id":549,"name":"Bandsintown Amplified LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://corp.bandsintown.com/privacy"},{"id":597,"name":"Better Banners A/S","purposeIds":[],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://betterbanners.com/en/privacy"},{"id":601,"name":"WebAds B.V","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://privacy.webads.eu/"},{"id":599,"name":"Maximus Live LLC","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[3],"policyUrl":"https://maximusx.com/privacy-policy/"},{"id":604,"name":"Join","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.teamjoin.fr/privacy.html","deletedDate":"2020-04-23T00:00:00Z"},{"id":606,"name":"Impactify ","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://impactify.io/privacy-policy/"},{"id":608,"name":"News and Media Holding, a.s.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.newsandmedia.sk/gdpr/"},{"id":602,"name":"Online Solution Int Limited","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2],"policyUrl":"https://adsafety.net/privacy.html"},{"id":591,"name":"Consumable, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://consumable.com/privacy-policy.html"},{"id":614,"name":"Market Resource Partners LLC","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.mrpfd.com/privacy-policy/"},{"id":615,"name":"Adsolutions BV","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.adsolutions.com/privacy-policy/"},{"id":607,"name":"ucfunnel Co., Ltd.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.ucfunnel.com/privacy-policy"},{"id":609,"name":"Predicio","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.predic.io/privacy"},{"id":617,"name":"Onfocus (Adagio)","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adagio.io/privacy"},{"id":620,"name":"Blue","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"http://www.getblue.io/privacy/"},{"id":610,"name":"Azerion Holding B.V.","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[2,3],"policyUrl":"https://azerion.com/business/privacy.html"},{"id":621,"name":"Seznam.cz, a.s.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://www.seznam.cz/ochranaudaju"},{"id":624,"name":"Norstat AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.norstatpanel.com/en/data-protection"},{"id":623,"name":"Adprime Media Inc. ","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adprimehealth.com/privacy/","deletedDate":"2020-06-17T00:00:00Z"},{"id":95,"name":"Lotame Solutions, inc","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[2],"policyUrl":"https://www.lotame.com/about-lotame/privacy/lotame-corporate-websites-privacy-policy/"},{"id":618,"name":"BEINTOO SPA","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.beintoo.com/privacy-cookie-policy/"},{"id":619,"name":"Capitaldata","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.capitaldata.fr/privacy"},{"id":625,"name":"BILENDI SA","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.maximiles.com/privacy-policy"},{"id":628,"name":": Tappx","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.tappx.com/en/privacy-policy/"},{"id":626,"name":"Hivestack Inc.","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[3],"policyUrl":"https://hivestack.com/privacy-policy"},{"id":631,"name":"Relay42 Netherlands B.V.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://relay42.com/privacy"},{"id":627,"name":"D-Edge","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.d-edge.com/privacy-policy/","deletedDate":"2020-07-06T00:00:00Z"},{"id":644,"name":"Gamoshi LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.gamoshi.com/privacy-policy"},{"id":639,"name":"Smile Wanted Group","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.smilewanted.com/privacy.php"},{"id":635,"name":"WebMediaRM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.webmediarm.com/vie_privee_et_opposition_en.php"},{"id":579,"name":"Ve Global","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.ve.com/privacy-policy"},{"id":645,"name":"Noster Finance S.L.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.finect.com/terminos-legales/politica-de-cookies"},{"id":653,"name":"Smartme Analytics","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"http://smartmeapp.com/info/smartme/aviso_legal.php","deletedDate":"2020-07-03T00:00:00Z"},{"id":613,"name":"Adserve.zone / Artworx AS","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://adserve.zone/adserveprivacypolicy.html"},{"id":573,"name":"Dailymotion SA","purposeIds":[2,3,4,5],"legIntPurposeIds":[1],"featureIds":[2],"policyUrl":"https://www.dailymotion.com/legal/privacy"},{"id":652,"name":"Skaze","purposeIds":[1,2],"legIntPurposeIds":[3,4,5],"featureIds":[1,2,3],"policyUrl":"http://www.skaze.fr/rgpd/"},{"id":646,"name":"Notify","purposeIds":[1,2],"legIntPurposeIds":[5],"featureIds":[1],"policyUrl":"https://notify-group.com/en/mentions-legales/"},{"id":648,"name":"TrueData Solutions, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.truedata.co/privacy-policy/"},{"id":647,"name":"Axel Springer Teaser Ad GmbH","purposeIds":[2],"legIntPurposeIds":[1,3,5],"featureIds":[],"policyUrl":"https://www.adup-tech.com/privacy"},{"id":654,"name":"GRAPHINIUM","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.graphinium.com/privacy/"},{"id":659,"name":"Research and Analysis of Media in Sweden AB","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www2.rampanel.com/privacy-policy/"},{"id":656,"name":"Think Clever Media","purposeIds":[1,2,3,4],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.contentignite.com/privacy-policy/"},{"id":504,"name":"Alive & Kicking Global Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.mcsaatchiplc.com/legal/privacy-cookies","deletedDate":"2020-07-27T00:00:00Z"},{"id":657,"name":"GP One GmbH","purposeIds":[],"legIntPurposeIds":[1,3,5],"featureIds":[3],"policyUrl":"https://www.gsi-one.org/de/privacy-policy.html"},{"id":655,"name":"Sportradar AG","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.sportradar.com/about-us/privacy/"},{"id":662,"name":"SoundCast","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://soundcast.fm/en/data-privacy"},{"id":665,"name":"Digital East GmbH","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.digitaleast.mobi/en/legal/privacy-policy/"},{"id":650,"name":"Telefonica Investigaci\u00f3n y Desarrollo S.A.U","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://www.cognitivemarketing.tid.es/"},{"id":666,"name":"BeOp","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://beop.io/privacy"},{"id":663,"name":"Mobsuccess","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.mobsuccess.com/en/privacy"},{"id":658,"name":"BLIINK SAS","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://bliink.io/privacy-policy"},{"id":667,"name":"Liftoff Mobile, Inc.","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[3],"policyUrl":"https://liftoff.io/privacy-policy/"},{"id":668,"name":"WhatRocks Inc. ","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.whatrocks.co/en/privacy-policy "},{"id":670,"name":"Timehop, Inc.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.timehop.com/privacy"},{"id":674,"name":"Duration Media, LLC.","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.durationmedia.net/privacy-policy"},{"id":675,"name":"Instreamatic inc.","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://instreamatic.com/privacy-policy/"},{"id":676,"name":"BusinessClick","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.businessclick.com/documents/RegulaminProgramuBusinessClick-2019.pdf"},{"id":677,"name":"Intercept Interactive Inc. dba Undertone","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.undertone.com/privacy/"},{"id":660,"name":"Schibsted Norge AS","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[2,3],"policyUrl":"https://static.vg.no/privacy/","deletedDate":"2019-09-16T00:00:00Z"},{"id":673,"name":"TTNET AS","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"http://www.programattik.com/en/privacy-policy.aspx"},{"id":664,"name":"adMarketplace, Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"https://www.admarketplace.com/privacy-policy/"},{"id":671,"name":"Mediaforce LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,3],"policyUrl":"http://casino.mindthebet.co.uk/themes/mindthebetv2-casino/privacy.php"},{"id":561,"name":"AuDigent","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://audigent.com/platform-privacy-policy"},{"id":682,"name":"Radio Net Media Limited","purposeIds":[1,2,3],"legIntPurposeIds":[5],"featureIds":[1,2,3],"policyUrl":"https://www.adtonos.com/service-privacy-policy/"},{"id":684,"name":"Blue Billywig BV","purposeIds":[],"legIntPurposeIds":[5],"featureIds":[],"policyUrl":"https://www.bluebillywig.com/privacy-statement/"},{"id":686,"name":"The MediaGrid Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://www.themediagrid.com/privacy-policy/"},{"id":685,"name":"Arkeero","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://arkeero.com/privacy-2/"},{"id":687,"name":"MISSENA","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://missena.com/confidentialite/"},{"id":690,"name":"Go.pl sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://go.pl/polityka-prywatnosci/"},{"id":691,"name":"Lifesight Pte. Ltd.","purposeIds":[1,2,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.lifesight.io/privacy-policy/"},{"id":697,"name":"ADWAYS SAS","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://www.adways.com/confidentialite/?lang=en"},{"id":681,"name":"MyTraffic","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://www.mytraffic.io/en/privacy"},{"id":649,"name":"adality GmbH","purposeIds":[],"legIntPurposeIds":[1,2],"featureIds":[1],"policyUrl":"https://adality.de/en/privacy/"},{"id":712,"name":"Inspired Mobile Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://byinspired.com/privacypolicy.pdf"},{"id":688,"name":"Effinity","purposeIds":[],"legIntPurposeIds":[1],"featureIds":[],"policyUrl":"https://www.effiliation.com/politique-de-confidentialite/"},{"id":702,"name":"Kwanko","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.kwanko.com/fr/rgpd/"},{"id":715,"name":"BidBerry SRL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.bidberrymedia.com/privacy-policy/"},{"id":713,"name":"Dataseat Ltd","purposeIds":[2,5],"legIntPurposeIds":[1,3,4],"featureIds":[],"policyUrl":"https://dataseat.com/privacy-policy"},{"id":716,"name":"OnAudience Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.onaudience.com/internet-advertising-privacy-policy"},{"id":708,"name":"Dugout Limited ","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://dugout.com/privacy-policy"},{"id":717,"name":"Audience Network","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.en.audiencenetwork.pl/internet-advertising-privacy-policy"},{"id":718,"name":"AppConsent Xchange","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://appconsent.io/en/privacy-policy"},{"id":720,"name":"AAX LLC","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[3],"policyUrl":"https://aax.media/privacy/"},{"id":678,"name":"Axonix LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://axonix.com/privacy-cookie-policy/"},{"id":719,"name":"Online Advertising Network Sp. z o.o.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://www.oan.pl/en/privacy-policy"},{"id":707,"name":"Dentsu Aegis Network Italia SpA","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.dentsuaegisnetwork.com/it/it/policies/info-cookie"},{"id":721,"name":"Beaconspark Ltd","purposeIds":[1,2,3],"legIntPurposeIds":[4,5],"featureIds":[1],"policyUrl":"https://www.engageya.com/privacy"},{"id":724,"name":"Between Exchange","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[2,3],"policyUrl":"https://en.betweenx.com/pdata.pdf"},{"id":728,"name":"Appier PTE Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.appier.com/privacy-policy/"},{"id":729,"name":"Cavai AS & UK ","purposeIds":[],"legIntPurposeIds":[3],"featureIds":[],"policyUrl":"https://cav.ai/privacy-policy/"},{"id":723,"name":"Adzymic Pte Ltd","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"http://www.adzymic.co/privacy"},{"id":737,"name":"Monet Engine Inc","purposeIds":[1,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://appmonet.com/privacy-policy/"},{"id":740,"name":"6Sense Insights, Inc.","purposeIds":[1],"legIntPurposeIds":[2,3,4,5],"featureIds":[1,2],"policyUrl":"https://6sense.com/privacy-policy/"},{"id":744,"name":"Vidazoo Ltd","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[2],"policyUrl":"https://vidazoo.gitbook.io/vidazoo-legal/privacy-policy"},{"id":731,"name":"GeistM Technologies LTD","purposeIds":[],"legIntPurposeIds":[3,4,5],"featureIds":[],"policyUrl":"https://www.geistm.com/privacy"},{"id":741,"name":"Brand Advance Limited","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.wearebrandadvance.com/website-privacy-policy"},{"id":734,"name":"Cint AB","purposeIds":[1,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://www.cint.com/participant-privacy-notice"},{"id":709,"name":"NC Audience Exchange, LLC (NewsIQ)","purposeIds":[1,2],"legIntPurposeIds":[3,5],"featureIds":[1,2],"policyUrl":"https://www.ncaudienceexchange.com/privacy/"},{"id":739,"name":"Blingby LLC","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://blingby.com/privacy"},{"id":732,"name":"Performax.cz, s.r.o.","purposeIds":[2,4,5],"legIntPurposeIds":[1,3],"featureIds":[2,3],"policyUrl":"https://reg.tiscali.cz/privacy-policy"},{"id":736,"name":"BidMachine Inc.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://explorestack.com/privacy-policy/"},{"id":738,"name":"adbility media GmbH","purposeIds":[2,3],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.adbility-media.com/datenschutzerklaerung/"},{"id":742,"name":"Audiencerate LTD","purposeIds":[],"legIntPurposeIds":[1,2,5],"featureIds":[],"policyUrl":"https://www.audiencerate.com/privacy/"},{"id":743,"name":"MOVIads Sp. z o.o. Sp. k.","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://moviads.pl/polityka-prywatnosci/"},{"id":746,"name":"Adxperience SAS","purposeIds":[2],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://adxperience.com/privacy-policy/"},{"id":747,"name":"Kairion GmbH","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://kairion.de/datenschutzbestimmungen/"},{"id":748,"name":"AUDIOMOB LTD","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[2,3],"policyUrl":"https://www.audiomob.io/privacy"},{"id":749,"name":"Good-Loop Ltd","purposeIds":[2],"legIntPurposeIds":[1,3,4,5],"featureIds":[],"policyUrl":"https://doc.good-loop.com/policy/privacy-policy.html"},{"id":754,"name":"DistroScale, Inc.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"http://www.distroscale.com/privacy-policy/"},{"id":756,"name":"Fandom, Inc.","purposeIds":[3],"legIntPurposeIds":[1,2,4,5],"featureIds":[],"policyUrl":"https://www.fandom.com/privacy-policy"},{"id":758,"name":"GfK Netherlands B.V.","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://gfkpanel.nl/privacy"},{"id":759,"name":"RevJet","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.revjet.com/privacy"},{"id":760,"name":"VEXPRO TECHNOLOGIES LTD","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2],"policyUrl":"https://onedash.com/privacy-policy.html"},{"id":761,"name":"Digiseg ApS","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[1],"policyUrl":"https://digiseg.io/privacy-center/"},{"id":763,"name":"Delidatax SL","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"https://www.delidatax.net/privacy.htm"},{"id":764,"name":"Lucidity","purposeIds":[1,3,4,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://golucidity.com/privacy-policy/"},{"id":765,"name":"Grabit Interactive Media Inc dba KERV Interctive","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[2],"policyUrl":"https://kervit.com/privacy-policy/"},{"id":766,"name":"ADCELL | Firstlead GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.adcell.de/agb#sector_6"},{"id":768,"name":"Global Media & Entertainment Limited","purposeIds":[1,2,3,4,5],"legIntPurposeIds":[],"featureIds":[1,2,3],"policyUrl":"http://global.com/privacy-policy/"},{"id":770,"name":"MARKETPERF CORP","purposeIds":[1,2,4],"legIntPurposeIds":[3,5],"featureIds":[2,3],"policyUrl":"https://www.marketperf.com/assets/images/app/marketperf/pdf/privacy-policy.pdf"},{"id":773,"name":"360e-com Sp. z o.o.","purposeIds":[1,2,3,5],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.clickonometrics.com/optout/"},{"id":775,"name":"SelectMedia International LTD","purposeIds":[1],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.selectmedia.asia/terms-and-privacy/"},{"id":778,"name":"Discover-Tech ltd","purposeIds":[2,5],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://discover-tech.io/dsp-privacy-policy/"},{"id":779,"name":"Adtarget Medya A.S.","purposeIds":[1,3],"legIntPurposeIds":[],"featureIds":[3],"policyUrl":"https://adtarget.com.tr/adtarget-privacy-policy-2020.pdf"},{"id":780,"name":"Aniview LTD","purposeIds":[1,2,3],"legIntPurposeIds":[],"featureIds":[],"policyUrl":"https://www.aniview.com/privacy-policy/"},{"id":781,"name":"FeedAd GmbH","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[2,3],"policyUrl":"https://feedad.com/privacy/"},{"id":784,"name":"Nubo LTD","purposeIds":[],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://www.recod3.com/privacypolicy.php"},{"id":786,"name":"TargetVideo GmbH","purposeIds":[],"legIntPurposeIds":[1,2,3,4,5],"featureIds":[],"policyUrl":"https://www.target-video.com/datenschutz/"},{"id":798,"name":"Adverticum cPlc.","purposeIds":[2,3,4],"legIntPurposeIds":[1,5],"featureIds":[],"policyUrl":"https://adverticum.net/english/privacy-and-data-processing-information/"},{"id":803,"name":"Click Tech Limited","purposeIds":[1],"legIntPurposeIds":[2,3],"featureIds":[1],"policyUrl":"https://en.yeahmobi.com/html/privacypolicy/"},{"id":808,"name":"Pure Local Media GmbH","purposeIds":[],"legIntPurposeIds":[3,5],"featureIds":[],"policyUrl":"https://purelocalmedia.de/?page_id=593"}]} \ No newline at end of file From 21b41ff22c9fce7823f061077dd00bc679ddd7b9 Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Thu, 20 Aug 2020 12:59:27 -0400 Subject: [PATCH 170/318] Enable geo activation of GDPR flag (#1427) --- config/config.go | 9 +++ exchange/exchange.go | 25 +++++++- exchange/exchange_test.go | 27 +++++--- .../exchangetest/gdpr-geo-eu-off-device.json | 64 +++++++++++++++++++ exchange/exchangetest/gdpr-geo-eu-off.json | 60 +++++++++++++++++ exchange/exchangetest/gdpr-geo-eu-on.json | 60 +++++++++++++++++ exchange/exchangetest/gdpr-geo-usa-off.json | 61 ++++++++++++++++++ exchange/exchangetest/gdpr-geo-usa-on.json | 61 ++++++++++++++++++ gdpr/impl.go | 19 ++++++ 9 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 exchange/exchangetest/gdpr-geo-eu-off-device.json create mode 100644 exchange/exchangetest/gdpr-geo-eu-off.json create mode 100644 exchange/exchangetest/gdpr-geo-eu-on.json create mode 100644 exchange/exchangetest/gdpr-geo-usa-off.json create mode 100644 exchange/exchangetest/gdpr-geo-usa-on.json diff --git a/config/config.go b/config/config.go index 7fc77855810..9e6b1370128 100755 --- a/config/config.go +++ b/config/config.go @@ -159,6 +159,11 @@ type GDPR struct { TCF1 TCF1 `mapstructure:"tcf1"` TCF2 TCF2 `mapstructure:"tcf2"` AMPException bool `mapstructure:"amp_exception"` + // EEACountries (EEA = European Economic Area) are a list of countries where we should assume GDPR applies. + // If the gdpr flag is unset in a request, but geo.country is set, we will assume GDPR applies if and only + // if the country matches one on this list. If both the GDPR flag and country are not set, we default + // to UsersyncIfAmbiguous + EEACountries []string `mapstructure:"eea_countries"` } func (cfg *GDPR) validate(errs configErrors) configErrors { @@ -903,6 +908,10 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("gdpr.tcf2.purpose_one_treatement.enabled", true) v.SetDefault("gdpr.tcf2.purpose_one_treatement.access_allowed", true) v.SetDefault("gdpr.amp_exception", false) + v.SetDefault("gdpr.eea_countries", []string{"ALA", "AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", + "FIN", "FRA", "GUF", "DEU", "GIB", "GRC", "GLP", "GGY", "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "LVA", + "LIE", "LTU", "LUX", "MLT", "MTQ", "MYT", "NLD", "NOR", "POL", "PRT", "REU", "ROU", "BLM", "MAF", "SPM", + "SVK", "SVN", "ESP", "SWE", "GBR"}) v.SetDefault("ccpa.enforce", false) v.SetDefault("lmt.enforce", true) v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") diff --git a/exchange/exchange.go b/exchange/exchange.go index cf5ec9cc000..53f4a7a3e1f 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -55,6 +55,7 @@ type exchange struct { UsersyncIfAmbiguous bool defaultTTLs config.DefaultTTLs privacyConfig config.Privacy + eeaCountries map[string]struct{} } // Container to pass out response ext data from the GetAllBids goroutines back into the main thread @@ -75,6 +76,10 @@ type bidResponseWrapper struct { func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currencies.RateConverter) Exchange { e := new(exchange) + var s struct{} + for _, c := range cfg.GDPR.EEACountries { + e.eeaCountries[c] = s + } e.adapterMap = newAdapterMap(client, cfg, infos, metricsEngine) e.cache = cache e.cacheTime = time.Duration(cfg.CacheURL.ExpectedTimeMillis) * time.Millisecond @@ -121,9 +126,27 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque e.me.RecordImps(impLabels) } + // Make our best guess if GDPR applies + usersyncIfAmbiguous := e.UsersyncIfAmbiguous + var geo *openrtb.Geo = nil + if bidRequest.User != nil && bidRequest.User.Geo != nil { + geo = bidRequest.User.Geo + } else if bidRequest.Device != nil && bidRequest.Device.Geo != nil { + geo = bidRequest.Device.Geo + } + if geo != nil { + // If we have a country set, and it is on the list, we assume GDPR applies if not set on the request. + // Otherwise we assume it does not apply as long as it appears "valid" (is 3 characters long). + if _, found := e.eeaCountries[strings.ToUpper(geo.Country)]; found { + usersyncIfAmbiguous = false + } else if len(geo.Country) == 3 { + // The country field is formatted properly as a three character country code + usersyncIfAmbiguous = true + } + } // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, e.UsersyncIfAmbiguous, e.privacyConfig) + cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, usersyncIfAmbiguous, e.privacyConfig) e.me.RecordRequestPrivacy(privacyLabels) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 545f04fd0ef..aad448f397f 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -909,6 +909,9 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { LMT: config.LMT{ Enforce: spec.EnforceLMT, }, + GDPR: config.GDPR{ + UsersyncIfAmbiguous: !spec.AssumeGDPRApplies, + }, } ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig) @@ -1026,15 +1029,22 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] } } + var s struct{} + eeac := make(map[string]struct{}) + for _, c := range []string{"FIN", "FRA", "GUF"} { + eeac[c] = s + } + return &exchange{ adapterMap: adapters, me: metricsConf.NewMetricsEngine(&config.Configuration{}, openrtb_ext.BidderList()), cache: &wellBehavedCache{}, cacheTime: 0, - gDPR: gdpr.AlwaysAllow{}, + gDPR: gdpr.AlwaysFail{}, currencyConverter: currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)), - UsersyncIfAmbiguous: false, + UsersyncIfAmbiguous: privacyConfig.GDPR.UsersyncIfAmbiguous, privacyConfig: privacyConfig, + eeaCountries: eeac, } } @@ -1882,12 +1892,13 @@ func TestUpdateHbPbCatDur(t *testing.T) { } type exchangeSpec struct { - IncomingRequest exchangeRequest `json:"incomingRequest"` - OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` - Response exchangeResponse `json:"response,omitempty"` - EnforceCCPA bool `json:"enforceCcpa"` - EnforceLMT bool `json:"enforceLmt"` - DebugLog *DebugLog `json:"debuglog,omitempty"` + IncomingRequest exchangeRequest `json:"incomingRequest"` + OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` + Response exchangeResponse `json:"response,omitempty"` + EnforceCCPA bool `json:"enforceCcpa"` + EnforceLMT bool `json:"enforceLmt"` + AssumeGDPRApplies bool `json:"assume_gdpr_applies"` + DebugLog *DebugLog `json:"debuglog,omitempty"` } type exchangeRequest struct { diff --git a/exchange/exchangetest/gdpr-geo-eu-off-device.json b/exchange/exchangetest/gdpr-geo-eu-off-device.json new file mode 100644 index 00000000000..fc655de8162 --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-eu-off-device.json @@ -0,0 +1,64 @@ +{ + "assume_gdpr_applies": false, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id" + }, + "device": { + "geo": { + "country": "FRA" + } + } +} + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + }, + "device": { + "geo": { + "country": "FRA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/gdpr-geo-eu-off.json b/exchange/exchangetest/gdpr-geo-eu-off.json new file mode 100644 index 00000000000..27a030f11fc --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-eu-off.json @@ -0,0 +1,60 @@ +{ + "assume_gdpr_applies": false, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "FRA" + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + "geo": { + "country": "FRA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/gdpr-geo-eu-on.json b/exchange/exchangetest/gdpr-geo-eu-on.json new file mode 100644 index 00000000000..4ec42fc6c70 --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-eu-on.json @@ -0,0 +1,60 @@ +{ + "assume_gdpr_applies": true, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "FRA" + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + "geo": { + "country": "FRA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/gdpr-geo-usa-off.json b/exchange/exchangetest/gdpr-geo-usa-off.json new file mode 100644 index 00000000000..d56c9318a56 --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-usa-off.json @@ -0,0 +1,61 @@ +{ + "assume_gdpr_applies": false, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "USA" + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "USA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/gdpr-geo-usa-on.json b/exchange/exchangetest/gdpr-geo-usa-on.json new file mode 100644 index 00000000000..f922be9ea4e --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-usa-on.json @@ -0,0 +1,61 @@ +{ + "assume_gdpr_applies": true, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "USA" + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "USA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/gdpr/impl.go b/gdpr/impl.go index 2deddc7b2ba..2fbd9c5a07c 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -228,3 +228,22 @@ func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext func (a AlwaysAllow) AMPException() bool { return false } + +// Exporting to allow for easy test setups +type AlwaysFail struct{} + +func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { + return false, nil +} + +func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { + return false, nil +} + +func (a AlwaysFail) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext.BidderName, PublisherID string, consent string) (bool, bool, bool, error) { + return false, false, false, nil +} + +func (a AlwaysFail) AMPException() bool { + return false +} From f4b0a7cfc95aba6a27e9aaf06a57716fc2057c76 Mon Sep 17 00:00:00 2001 From: guscarreon Date: Thu, 20 Aug 2020 14:19:37 -0400 Subject: [PATCH 171/318] Validate External Cache Host (#1422) * first draft * Little tweaks * Scott's review part 1 * Scott's review corrections part 2 * Scotts refactor * correction in config_test.go * Correction and refactor * Multiple return statements * Test case refactor Co-authored-by: Gus Carreon Co-authored-by: Gus Carreon Co-authored-by: Gus Carreon --- config/config.go | 36 +++++++++++++++++ config/config_test.go | 89 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 9e6b1370128..e3b7d8ebda0 100755 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "errors" "fmt" "net/url" "reflect" @@ -111,6 +112,7 @@ func (cfg *Configuration) validate() configErrors { errs = cfg.CurrencyConverter.validate(errs) errs = validateAdapters(cfg.Adapters, errs) errs = cfg.Debug.validate(errs) + errs = cfg.ExtCacheURL.validate(errs) return errs } @@ -128,6 +130,40 @@ func (cfg *AuctionTimeouts) validate(errs configErrors) configErrors { return errs } +func (data *ExternalCache) validate(errs configErrors) configErrors { + if data.Host == "" && data.Path == "" { + // Both host and path can be blank. No further validation needed + return errs + } + + // Either host or path or both not empty, validate. + if data.Host == "" && data.Path != "" || data.Host != "" && data.Path == "" { + return append(errs, errors.New("External cache Host and Path must both be specified")) + } + if strings.HasSuffix(data.Host, "/") { + return append(errs, errors.New(fmt.Sprintf("External cache Host '%s' must not end with a path separator", data.Host))) + } + if strings.ContainsAny(data.Host, "://") { + return append(errs, errors.New(fmt.Sprintf("External cache Host must not specify a protocol. '%s'", data.Host))) + } + if !strings.HasPrefix(data.Path, "/") { + return append(errs, errors.New(fmt.Sprintf("External cache Path '%s' must begin with a path separator", data.Path))) + } + + urlObj, err := url.Parse("https://" + data.Host + data.Path) + if err != nil { + return append(errs, errors.New(fmt.Sprintf("External cache Path validation error: %s ", err.Error()))) + } + if urlObj.Host != data.Host { + return append(errs, errors.New(fmt.Sprintf("External cache Host '%s' is invalid", data.Host))) + } + if urlObj.Path != data.Path { + return append(errs, errors.New("External cache Path is invalid")) + } + + return errs +} + // LimitAuctionTimeout returns the min of requested or cfg.MaxAuctionTimeout. // Both values treat "0" as "infinite". func (cfg *AuctionTimeouts) LimitAuctionTimeout(requested time.Duration) time.Duration { diff --git a/config/config_test.go b/config/config_test.go index 4774d9d6e46..3da3f72137b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -14,6 +14,91 @@ import ( "github.com/stretchr/testify/assert" ) +func TestExternalCacheURLValidate(t *testing.T) { + testCases := []struct { + desc string + data ExternalCache + expErrors int + }{ + { + desc: "With http://", + data: ExternalCache{Host: "http://www.google.com", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "Without http://", + data: ExternalCache{Host: "www.google.com", Path: "/path/v1"}, + expErrors: 0, + }, + { + desc: "No scheme but '//' prefix", + data: ExternalCache{Host: "//www.google.com", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "// appears twice", + data: ExternalCache{Host: "//www.google.com//", Path: "path/v1"}, + expErrors: 1, + }, + { + desc: "Host has an only // value", + data: ExternalCache{Host: "//", Path: "path/v1"}, + expErrors: 1, + }, + { + desc: "only scheme host, valid path", + data: ExternalCache{Host: "http://", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "No host, path only", + data: ExternalCache{Host: "", Path: "path/v1"}, + expErrors: 1, + }, + { + desc: "No host, nor path", + data: ExternalCache{Host: "", Path: ""}, + expErrors: 0, + }, + { + desc: "Invalid http at the end", + data: ExternalCache{Host: "www.google.com", Path: "http://"}, + expErrors: 1, + }, + { + desc: "Host has an unknown scheme", + data: ExternalCache{Host: "unknownscheme://host", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "Wrong colon side in scheme", + data: ExternalCache{Host: "http//:www.appnexus.com", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "Missing '/' in scheme", + data: ExternalCache{Host: "http:/www.appnexus.com", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "host with scheme, no path", + data: ExternalCache{Host: "http://www.appnexus.com", Path: ""}, + expErrors: 1, + }, + { + desc: "scheme, no host nor path", + data: ExternalCache{Host: "http://", Path: ""}, + expErrors: 1, + }, + } + for _, test := range testCases { + var errs configErrors + errs = test.data.validate(errs) + + assert.Equal(t, test.expErrors, len(errs), "Test case threw unexpected number of errors. Desc: %s errMsg = %v \n", test.desc, errs) + } +} + func TestDefaults(t *testing.T) { v := viper.New() SetupViper(v, "") @@ -66,7 +151,7 @@ cache: query: uuid=%PBS_CACHE_UUID% external_cache: host: www.externalprebidcache.net - path: endpoints/cache + path: /endpoints/cache http_client: max_connections_per_host: 10 max_idle_connections: 500 @@ -223,7 +308,7 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "cache.host", cfg.CacheURL.Host, "prebidcache.net") cmpStrings(t, "cache.query", cfg.CacheURL.Query, "uuid=%PBS_CACHE_UUID%") cmpStrings(t, "external_cache.host", cfg.ExtCacheURL.Host, "www.externalprebidcache.net") - cmpStrings(t, "external_cache.path", cfg.ExtCacheURL.Path, "endpoints/cache") + cmpStrings(t, "external_cache.path", cfg.ExtCacheURL.Path, "/endpoints/cache") cmpInts(t, "http_client.max_connections_per_host", cfg.Client.MaxConnsPerHost, 10) cmpInts(t, "http_client.max_idle_connections", cfg.Client.MaxIdleConns, 500) cmpInts(t, "http_client.max_idle_connections_per_host", cfg.Client.MaxIdleConnsPerHost, 20) From 80d557ece7ae79413755db8021e9fd0559b1954c Mon Sep 17 00:00:00 2001 From: hhhjort <31041505+hhhjort@users.noreply.github.com> Date: Thu, 20 Aug 2020 15:36:33 -0400 Subject: [PATCH 172/318] Fixes bug (#1448) * Fixes bug * shortens list --- exchange/exchange.go | 1 + exchange/exchange_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/exchange/exchange.go b/exchange/exchange.go index 53f4a7a3e1f..e465a78389b 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -76,6 +76,7 @@ type bidResponseWrapper struct { func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, infos adapters.BidderInfos, gDPR gdpr.Permissions, currencyConverter *currencies.RateConverter) Exchange { e := new(exchange) + e.eeaCountries = make(map[string]struct{}, len(cfg.GDPR.EEACountries)) var s struct{} for _, c := range cfg.GDPR.EEACountries { e.eeaCountries[c] = s diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index aad448f397f..a6f69f70c59 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -48,6 +48,9 @@ func TestNewExchange(t *testing.T) { ExpectedTimeMillis: 20, }, Adapters: blankAdapterConfig(openrtb_ext.BidderList()), + GDPR: config.GDPR{ + EEACountries: []string{"FIN", "FRA", "GUF"}, + }, } currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) From d66338035f232aef73099ae1c257628142c62e2c Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Mon, 24 Aug 2020 13:43:02 -0700 Subject: [PATCH 173/318] Added adpod_id to request extension (#1444) * Added adpod_id to request -> ext -> appnexus and modified requests splitting based on pod * Unit test fix * Unit test fix * Minor unit test fixes * Code refactoring * Minor code and unit tests refactoring * Unit tests refactoring Co-authored-by: Veronika Solovei --- adapters/appnexus/appnexus.go | 62 ++++- adapters/appnexus/appnexus_test.go | 229 ++++++++++++++++++ .../video/simple-video.json | 132 ---------- 3 files changed, 283 insertions(+), 140 deletions(-) delete mode 100644 adapters/appnexus/appnexusplatformtest/video/simple-video.json diff --git a/adapters/appnexus/appnexus.go b/adapters/appnexus/appnexus.go index 9bec9bf1e3b..334817ebca7 100644 --- a/adapters/appnexus/appnexus.go +++ b/adapters/appnexus/appnexus.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/rand" "net/http" "strconv" "strings" @@ -95,10 +96,11 @@ type appnexusBidExt struct { } type appnexusReqExtAppnexus struct { - IncludeBrandCategory *bool `json:"include_brand_category,omitempty"` - BrandCategoryUniqueness *bool `json:"brand_category_uniqueness,omitempty"` - IsAMP int `json:"is_amp,omitempty"` - HeaderBiddingSource int `json:"hb_source,omitempty"` + IncludeBrandCategory *bool `json:"include_brand_category,omitempty"` + BrandCategoryUniqueness *bool `json:"brand_category_uniqueness,omitempty"` + IsAMP int `json:"is_amp,omitempty"` + HeaderBiddingSource int `json:"hb_source,omitempty"` + AdPodId string `json:"adpod_id,omitempty"` } // Full request extension including appnexus extension object @@ -354,14 +356,56 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada } reqExt.Appnexus.IsAMP = isAMP reqExt.Appnexus.HeaderBiddingSource = a.hbSource + isVIDEO + + imps := request.Imp + + // For long form requests adpod_id must be sent downstream. + // Adpod id is a unique identifier for pod + // All impressions in the same pod must have the same pod id in request extension + // For this all impressions in request should belong to the same pod + // If impressions number per pod is more than maxImpsPerReq - divide those imps to several requests but keep pod id the same + if isVIDEO == 1 { + podImps := groupByPods(imps) + + requests := make([]*adapters.RequestData, 0, len(podImps)) + for _, podImps := range podImps { + reqExt.Appnexus.AdPodId = generatePodId() + + reqs, errors := splitRequests(podImps, request, reqExt, thisURI, errs) + requests = append(requests, reqs...) + errs = append(errs, errors...) + } + return requests, errs + } + + return splitRequests(imps, request, reqExt, thisURI, errs) +} + +func generatePodId() string { + val := rand.Int63() + return fmt.Sprint(val) +} + +func groupByPods(imps []openrtb.Imp) map[string]([]openrtb.Imp) { + // find number of pods in response + podImps := make(map[string][]openrtb.Imp) + for _, imp := range imps { + pod := strings.Split(imp.ID, "_")[0] + podImps[pod] = append(podImps[pod], imp) + } + return podImps +} + +func marshalAndSetRequestExt(request *openrtb.BidRequest, requestExtension appnexusReqExt, errs []error) { var err error - request.Ext, err = json.Marshal(reqExt) + request.Ext, err = json.Marshal(requestExtension) if err != nil { errs = append(errs, err) - return nil, errs } +} + +func splitRequests(imps []openrtb.Imp, request *openrtb.BidRequest, requestExtension appnexusReqExt, uri string, errs []error) ([]*adapters.RequestData, []error) { - imps := request.Imp // Initial capacity for future array of requests, memory optimization. // Let's say there are 35 impressions and limit impressions per request equals to 10. // In this case we need to create 4 requests with 10, 10, 10 and 5 impressions. @@ -375,6 +419,8 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada headers.Add("Content-Type", "application/json;charset=utf-8") headers.Add("Accept", "application/json") + marshalAndSetRequestExt(request, requestExtension, errs) + for impsLeft { endInd := startInd + maxImpsPerReq @@ -393,7 +439,7 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada resArr = append(resArr, &adapters.RequestData{ Method: "POST", - Uri: thisURI, + Uri: uri, Body: reqJSON, Headers: headers, }) diff --git a/adapters/appnexus/appnexus_test.go b/adapters/appnexus/appnexus_test.go index bf49374940a..26380406624 100644 --- a/adapters/appnexus/appnexus_test.go +++ b/adapters/appnexus/appnexus_test.go @@ -4,9 +4,11 @@ import ( "bytes" "context" "encoding/json" + "github.com/stretchr/testify/assert" "io/ioutil" "net/http" "net/http/httptest" + "regexp" "testing" "time" @@ -38,6 +40,233 @@ func TestMemberQueryParam(t *testing.T) { } } +func TestVideoSinglePod(t *testing.T) { + var a AppNexusAdapter + a.URI = "http://test.com/openrtb2" + a.hbSource = 5 + + var reqInfo adapters.ExtraRequestInfo + reqInfo.PbsEntryPoint = "video" + + var req openrtb.BidRequest + req.ID = "test_id" + + reqExt := `{"prebid":{}}` + impExt := `{"bidder":{"placementId":123}}` + req.Ext = []byte(reqExt) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)}) + + result, err := a.MakeRequests(&req, &reqInfo) + + assert.Empty(t, err, "Errors array should be empty") + assert.Len(t, result, 1, "Only one request should be returned") + + var error error + var reqData *openrtb.BidRequest + error = json.Unmarshal(result[0].Body, &reqData) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt *appnexusReqExt + error = json.Unmarshal(reqData.Ext, &reqDataExt) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + regMatch, matchErr := regexp.Match(`[0-9]19`, []byte(reqDataExt.Appnexus.AdPodId)) + assert.NoError(t, matchErr, "Regex match error should be nil") + assert.True(t, regMatch, "AdPod id doesn't present in Appnexus extension or has incorrect format") +} + +func TestVideoSinglePodManyImps(t *testing.T) { + var a AppNexusAdapter + a.URI = "http://test.com/openrtb2" + a.hbSource = 5 + + var reqInfo adapters.ExtraRequestInfo + reqInfo.PbsEntryPoint = "video" + + var req openrtb.BidRequest + req.ID = "test_id" + + reqExt := `{"prebid":{}}` + impExt := `{"bidder":{"placementId":123}}` + req.Ext = []byte(reqExt) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_3", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_4", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_5", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_6", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_7", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_8", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_9", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_10", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_11", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_12", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_13", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_14", Ext: []byte(impExt)}) + + res, err := a.MakeRequests(&req, &reqInfo) + + assert.Empty(t, err, "Errors array should be empty") + assert.Len(t, res, 2, "Two requests should be returned") + + var error error + var reqData1 *openrtb.BidRequest + error = json.Unmarshal(res[0].Body, &reqData1) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt1 *appnexusReqExt + error = json.Unmarshal(reqData1.Ext, &reqDataExt1) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + adPodId1 := reqDataExt1.Appnexus.AdPodId + + var reqData2 *openrtb.BidRequest + error = json.Unmarshal(res[1].Body, &reqData2) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt2 *appnexusReqExt + error = json.Unmarshal(reqData2.Ext, &reqDataExt2) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + adPodId2 := reqDataExt2.Appnexus.AdPodId + + assert.Equal(t, adPodId1, adPodId2, "AdPod id is not the same for the same pod") +} + +func TestVideoTwoPods(t *testing.T) { + var a AppNexusAdapter + a.URI = "http://test.com/openrtb2" + a.hbSource = 5 + + var reqInfo adapters.ExtraRequestInfo + reqInfo.PbsEntryPoint = "video" + + var req openrtb.BidRequest + req.ID = "test_id" + + reqExt := `{"prebid":{}}` + impExt := `{"bidder":{"placementId":123}}` + req.Ext = []byte(reqExt) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)}) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_2", Ext: []byte(impExt)}) + + res, err := a.MakeRequests(&req, &reqInfo) + + assert.Empty(t, err, "Errors array should be empty") + assert.Len(t, res, 2, "Two request should be returned") + + var error error + var reqData1 *openrtb.BidRequest + error = json.Unmarshal(res[0].Body, &reqData1) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt1 *appnexusReqExt + error = json.Unmarshal(reqData1.Ext, &reqDataExt1) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + adPodId1 := reqDataExt1.Appnexus.AdPodId + + var reqData2 *openrtb.BidRequest + error = json.Unmarshal(res[1].Body, &reqData2) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt2 *appnexusReqExt + error = json.Unmarshal(reqData2.Ext, &reqDataExt2) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + adPodId2 := reqDataExt2.Appnexus.AdPodId + + assert.NotEqual(t, adPodId1, adPodId2, "AdPod id should be different for different pods") +} + +func TestVideoTwoPodsManyImps(t *testing.T) { + var a AppNexusAdapter + a.URI = "http://test.com/openrtb2" + a.hbSource = 5 + + var reqInfo adapters.ExtraRequestInfo + reqInfo.PbsEntryPoint = "video" + + var req openrtb.BidRequest + req.ID = "test_id" + + reqExt := `{"prebid":{}}` + impExt := `{"bidder":{"placementId":123}}` + req.Ext = []byte(reqExt) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)}) + + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_0", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_1", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_2", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_3", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_4", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_5", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_6", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_7", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_8", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_9", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_10", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_11", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_12", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_13", Ext: []byte(impExt)}) + req.Imp = append(req.Imp, openrtb.Imp{ID: "2_14", Ext: []byte(impExt)}) + + res, err := a.MakeRequests(&req, &reqInfo) + + assert.Empty(t, err, "Errors array should be empty") + assert.Len(t, res, 3, "Three requests should be returned") + + var error error + var reqData1 *openrtb.BidRequest + error = json.Unmarshal(res[0].Body, &reqData1) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt1 *appnexusReqExt + error = json.Unmarshal(reqData1.Ext, &reqDataExt1) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + var reqData2 *openrtb.BidRequest + error = json.Unmarshal(res[1].Body, &reqData2) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt2 *appnexusReqExt + error = json.Unmarshal(reqData2.Ext, &reqDataExt2) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + var reqData3 *openrtb.BidRequest + error = json.Unmarshal(res[2].Body, &reqData3) + assert.NoError(t, error, "Response body unmarshalling error should be nil") + + var reqDataExt3 *appnexusReqExt + error = json.Unmarshal(reqData3.Ext, &reqDataExt3) + assert.NoError(t, error, "Response ext unmarshalling error should be nil") + + adPodId1 := reqDataExt1.Appnexus.AdPodId + adPodId2 := reqDataExt2.Appnexus.AdPodId + adPodId3 := reqDataExt3.Appnexus.AdPodId + + podIds := make(map[string]int) + podIds[adPodId1] = podIds[adPodId1] + 1 + podIds[adPodId2] = podIds[adPodId2] + 1 + podIds[adPodId3] = podIds[adPodId3] + 1 + + assert.Len(t, podIds, 2, "Incorrect number of unique pod ids") +} + // ---------------------------------------------------------------------------- // Code below this line tests the legacy, non-openrtb code flow. It can be deleted after we // clean up the existing code and make everything openrtb. diff --git a/adapters/appnexus/appnexusplatformtest/video/simple-video.json b/adapters/appnexus/appnexusplatformtest/video/simple-video.json deleted file mode 100644 index 7ee192be2c1..00000000000 --- a/adapters/appnexus/appnexusplatformtest/video/simple-video.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "mockBidRequest": { - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "video": { - "mimes": ["video/mp4"], - "minduration": 15, - "maxduration": 30, - "protocols": [2, 3, 5, 6, 7, 8], - "w": 940, - "h": 560 - }, - "ext": { - "bidder": { - "placement_id": 1 - } - } - } - ] - }, - - "httpCalls": [ - { - "expectedRequest": { - "uri": "http://ib.adnxs.com/openrtb2", - "body": { - "id": "test-request-id", - "ext": { - "appnexus": { - "hb_source": 9 - }, - "prebid": {} - }, - "imp": [ - { - "id": "test-imp-id", - "video": { - "mimes": ["video/mp4"], - "minduration": 15, - "maxduration": 30, - "protocols": [2, 3, 5, 6, 7, 8], - "w": 940, - "h": 560 - }, - "ext": { - "appnexus": { - "placement_id": 1 - } - } - } - ] - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "test-request-id", - "seatbid": [ - { - "seat": "958", - "bid": [{ - "id": "7706636740145184841", - "impid": "test-imp-id", - "price": 0.500000, - "adid": "29681110", - "adm": "some-test-ad", - "adomain": ["appnexus.com"], - "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110", - "cid": "958", - "crid": "29681110", - "h": 250, - "w": 300, - "cat": ["IAB9-1"], - "ext": { - "appnexus": { - "brand_id": 9, - "brand_category_id": 9, - "auction_id": 8189378542222915032, - "bid_ad_type": 1, - "bidder_id": 2, - "ranking_price": 0.000000, - "deal_priority": 5 - } - } - }] - } - ], - "bidid": "5778926625248726496", - "cur": "USD" - } - } - } - ], - - "expectedBidResponses": [ - { - "currency": "USD", - "bids": [ - { - "bid": { - "id": "7706636740145184841", - "impid": "test-imp-id", - "price": 0.5, - "adm": "some-test-ad", - "adid": "29681110", - "adomain": ["appnexus.com"], - "iurl": "http://nym1-ib.adnxs.com/cr?id=29681110", - "cid": "958", - "crid": "29681110", - "w": 300, - "h": 250, - "cat": ["IAB5-3"], - "ext": { - "appnexus": { - "brand_id": 9, - "brand_category_id": 9, - "auction_id": 8189378542222915032, - "bid_ad_type": 1, - "bidder_id": 2, - "ranking_price": 0.000000, - "deal_priority": 5 - } - } - }, - "type": "video" - } - ] - } - ] - } \ No newline at end of file From 30ef8581806f5957c4417f05cd305e709d53a92e Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Mon, 24 Aug 2020 23:49:06 +0300 Subject: [PATCH 174/318] Adform adapter: additional targeting params added (#1424) --- adapters/adform/adform.go | 21 +++++++++++++++++++++ adapters/adform/adform_test.go | 30 ++++++++++++++++++++++-------- adapters/adform/params_test.go | 8 ++++++++ openrtb_ext/imp_adform.go | 11 +++++++---- static/bidder-params/adform.json | 14 ++++++++++++++ 5 files changed, 72 insertions(+), 12 deletions(-) diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go index 69f1c12f073..5881f4ab86e 100644 --- a/adapters/adform/adform.go +++ b/adapters/adform/adform.go @@ -43,6 +43,7 @@ type adformRequest struct { digitrust *adformDigitrust currency string eids string + url string } type adformDigitrust struct { @@ -61,6 +62,9 @@ type adformAdUnit struct { PriceType string `json:"priceType,omitempty"` KeyValues string `json:"mkv,omitempty"` KeyWords string `json:"mkw,omitempty"` + CDims string `json:"cdims,omitempty"` + MinPrice float64 `json:"minp,omitempty"` + Url string `json:"url,omitempty"` bidId string adUnitCode string @@ -284,6 +288,10 @@ func (r *adformRequest) buildAdformUrl(a *AdformAdapter) string { parameters.Add("eids", r.eids) } + if r.url != "" { + parameters.Add("url", r.url) + } + URL := *a.URL URL.RawQuery = parameters.Encode() @@ -302,6 +310,12 @@ func (r *adformRequest) buildAdformUrl(a *AdformAdapter) string { if adUnit.KeyWords != "" { buffer.WriteString(fmt.Sprintf("&mkw=%s", adUnit.KeyWords)) } + if adUnit.CDims != "" { + buffer.WriteString(fmt.Sprintf("&cdims=%s", adUnit.CDims)) + } + if adUnit.MinPrice > 0 { + buffer.WriteString(fmt.Sprintf("&minp=%.2f", adUnit.MinPrice)) + } adUnitsParams = append(adUnitsParams, base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(buffer.Bytes())) } @@ -407,6 +421,8 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro adUnits := make([]*adformAdUnit, 0, len(request.Imp)) errors := make([]error, 0, len(request.Imp)) secure := false + url := "" + for _, imp := range request.Imp { params, _, _, err := jsonparser.Get(imp.Ext, "bidder") if err != nil { @@ -441,6 +457,10 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro secure = true } + if url == "" { + url = adformAdUnit.Url + } + adformAdUnit.bidId = imp.ID adformAdUnit.adUnitCode = imp.ID adUnits = append(adUnits, &adformAdUnit) @@ -520,6 +540,7 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro digitrust: digitrust, currency: requestCurrency, eids: eids, + url: url, }, errors } diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index 2fca7d1722d..f227776207d 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -35,6 +35,9 @@ type aTagInfo struct { keyValues string keyWords string code string + cdims string + url string + minp float64 price float64 content string @@ -320,9 +323,9 @@ func createTestData(secure bool) aBidInfo { tid: "transaction-id", buyerUID: "user-id", tags: []aTagInfo{ - {mid: 32344, keyValues: "color:red,age:30-40", keyWords: "red,blue", priceType: "gross", code: "code1", price: 1.23, content: "banner-content1", dealId: "dealId1", creativeId: "creativeId1"}, - {mid: 32345, priceType: "net", code: "code2"}, // no bid for ad unit - {mid: 32346, code: "code3", price: 1.24, content: "banner-content2", dealId: "dealId2"}, + {mid: 32344, keyValues: "color:red,age:30-40", keyWords: "red,blue", cdims: "300x300,400x200", priceType: "gross", code: "code1", price: 1.23, content: "banner-content1", dealId: "dealId1", creativeId: "creativeId1"}, + {mid: 32345, priceType: "net", code: "code2", minp: 23.1, cdims: "300x200"}, // no bid for ad unit + {mid: 32346, code: "code3", price: 1.24, content: "banner-content2", dealId: "dealId2", url: "https://adform.com?a=b"}, }, secure: secure, currency: "EUR", @@ -519,11 +522,22 @@ func getUserExt() []byte { } func formatAdUnitJson(tag aTagInfo) string { - return fmt.Sprintf("{ \"mid\": %d%s%s%s}", + return fmt.Sprintf("{ \"mid\": %d%s%s%s%s%s%s}", tag.mid, formatAdUnitParam("priceType", tag.priceType), formatAdUnitParam("mkv", tag.keyValues), - formatAdUnitParam("mkw", tag.keyWords)) + formatAdUnitParam("mkw", tag.keyWords), + formatAdUnitParam("cdims", tag.cdims), + formatAdUnitParam("url", tag.url), + formatDemicalAdUnitParam("minp", tag.minp)) +} + +func formatDemicalAdUnitParam(fieldName string, fieldValue float64) string { + if fieldValue > 0 { + return fmt.Sprintf(", \"%s\": %.2f", fieldName, fieldValue) + } + + return "" } func formatAdUnitParam(fieldName string, fieldValue string) string { @@ -547,10 +561,10 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request, isOpenRtb boo var midsWithCurrency = "" var queryString = "" if isOpenRtb { - midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9RVVSJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZQ&bWlkPTMyMzQ1JnJjdXI9RVVS&bWlkPTMyMzQ2JnJjdXI9RVVS" - queryString = "CC=1&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&eids=eyJ0ZXN0LmNvbSI6eyJvdGhlcl91c2VyX2lkIjpbMF0sInNvbWVfdXNlcl9pZCI6WzFdfSwidGVzdDIub3JnIjp7Im90aGVyX3VzZXJfaWQiOlsyXX19&fd=1&gdpr=1&gdpr_consent=abc&ip=111.111.111.111&pt=gross&rp=4&stid=transaction-id&" + midsWithCurrency + midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9RVVSJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9RVVSJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9RVVS" + queryString = "CC=1&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&eids=eyJ0ZXN0LmNvbSI6eyJvdGhlcl91c2VyX2lkIjpbMF0sInNvbWVfdXNlcl9pZCI6WzFdfSwidGVzdDIub3JnIjp7Im90aGVyX3VzZXJfaWQiOlsyXX19&fd=1&gdpr=1&gdpr_consent=abc&ip=111.111.111.111&pt=gross&rp=4&stid=transaction-id&url=https%3A%2F%2Fadform.com%3Fa%3Db&" + midsWithCurrency } else { - midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9VVNEJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZQ&bWlkPTMyMzQ1JnJjdXI9VVNE&bWlkPTMyMzQ2JnJjdXI9VVNE" // no way to pass currency in legacy adapter + midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9VVNEJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9VVNEJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9VVNE" // no way to pass currency in legacy adapter queryString = "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&" + midsWithCurrency } diff --git a/adapters/adform/params_test.go b/adapters/adform/params_test.go index ae0a02b6a97..b392463f426 100644 --- a/adapters/adform/params_test.go +++ b/adapters/adform/params_test.go @@ -48,6 +48,10 @@ var validParams = []string{ `{"mid":"123","mkv":"color:"}`, `{"mid":"123","mkw":"green,male"}`, `{"mid":"123","mkv":" ","mkw":" "}`, + `{"mid":"123","cdims":"500x300,400x200","mkw":" "}`, + `{"mid":"123","cdims":"500x300","mkv":" ","mkw":" "}`, + `{"mid":"123","minp":2.1}`, + `{"mid":"123","url":"https://adform.com/page"}`, } var invalidParams = []string{ @@ -66,4 +70,8 @@ var invalidParams = []string{ `{"mid":"123","mkv":"color:blue,l&ngth:350"}`, `{"mid":"123","mkv":"color::blue"}`, `{"mid":"123","mkw":"fem&le"}`, + `{"mid":"123","minp":"2.1"}`, + `{"mid":"123","cdims":"500x300:400:200","mkw":" "}`, + `{"mid":"123","cdims":"500x300,400:200","mkv":" ","mkw":" "}`, + `{"mid":"123","url":10}`, } diff --git a/openrtb_ext/imp_adform.go b/openrtb_ext/imp_adform.go index 3e7c1a7261e..3206ece7c9b 100644 --- a/openrtb_ext/imp_adform.go +++ b/openrtb_ext/imp_adform.go @@ -1,8 +1,11 @@ package openrtb_ext type ExtImpAdform struct { - MasterTagId string `json:"mid"` - PriceType string `json:"priceType,omitempty"` - KeyValues string `json:"mkv,omitempty"` - KeyWords string `json:"mkw,omitempty"` + MasterTagId string `json:"mid"` + PriceType string `json:"priceType,omitempty"` + KeyValues string `json:"mkv,omitempty"` + KeyWords string `json:"mkw,omitempty"` + CDims string `json:"cdims,omitempty"` + MinPrice float64 `json:"minp,omitempty"` + Url string `json:"url,omitempty"` } diff --git a/static/bidder-params/adform.json b/static/bidder-params/adform.json index 67f09623ee4..f0b8c7a6be0 100644 --- a/static/bidder-params/adform.json +++ b/static/bidder-params/adform.json @@ -22,6 +22,20 @@ "type": "string", "description": "Comma-separated keywords. Forbidden symbols: &.", "pattern": "^[^&]*$" + }, + "cdims": { + "type": "string", + "description": "Comma-separated creative dimentions.", + "pattern": "(^\\d+x\\d+)(,\\d+x\\d+)*$" + }, + "minp": { + "type": "number", + "description": "The minimum CPM price.", + "minimum": 0 + }, + "url": { + "type": "string", + "description": "Custom URL for targeting." } }, "required": ["mid"] From 9dbd0083704315ac80721da30823753ee146bf19 Mon Sep 17 00:00:00 2001 From: Rob Hazan Date: Mon, 24 Aug 2020 17:28:42 -0400 Subject: [PATCH 175/318] Fix minor error message spelling mistake "vastml" -> "vastxml" (#1455) --- .../openrtb2/sample-requests/invalid-whole/cache-nothing.json | 2 +- openrtb_ext/request.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json b/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json index d4b875498ae..f256e4eb34c 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/cache-nothing.json @@ -1,5 +1,5 @@ { - "message": "Invalid request: request.ext is invalid: request.ext.prebid.cache requires one of the \"bids\" or \"vastml\" properties\n", + "message": "Invalid request: request.ext is invalid: request.ext.prebid.cache requires one of the \"bids\" or \"vastxml\" properties\n", "requestPayload": { "id": "some-request-id", "site": { diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 23daaf0f76e..d6edf47f939 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -67,7 +67,7 @@ func (ert *ExtRequestPrebidCache) UnmarshalJSON(b []byte) error { } if proxy.Bids == nil && proxy.VastXML == nil { - return errors.New(`request.ext.prebid.cache requires one of the "bids" or "vastml" properties`) + return errors.New(`request.ext.prebid.cache requires one of the "bids" or "vastxml" properties`) } *ert = ExtRequestPrebidCache(proxy) From 055ab8062c29038a3ac451e119f07d002ff57498 Mon Sep 17 00:00:00 2001 From: Cameron Rice <37162584+camrice@users.noreply.github.com> Date: Tue, 25 Aug 2020 08:37:04 -0700 Subject: [PATCH 176/318] Fixing comment for usage of deal priority field (#1451) --- adapters/bidder.go | 2 +- exchange/bidder.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/bidder.go b/adapters/bidder.go index 627caf67344..41218aa6a2f 100644 --- a/adapters/bidder.go +++ b/adapters/bidder.go @@ -108,7 +108,7 @@ func NewBidderResponse() *BidderResponse { // TypedBid.Bid.Ext will become "response.seatbid[i].bid.ext.bidder" in the final OpenRTB response. // TypedBid.BidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response. // TypedBid.BidVideo will become "response.seatbid[i].bid.ext.prebid.video" in the final OpenRTB response. -// TypedBid.DealPriority will become "response.seatbid[i].bid.dealPriority" in the final OpenRTB response. +// TypedBid.DealPriority is optionally provided by adapters and used internally by the exchange to support deal targeted campaigns. type TypedBid struct { Bid *openrtb.Bid BidType openrtb_ext.BidType diff --git a/exchange/bidder.go b/exchange/bidder.go index decad8ccf2f..5924e39b031 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -57,7 +57,7 @@ type adaptedBidder interface { // pbsOrtbBid.bidType will become "response.seatbid[i].bid.ext.prebid.type" in the final OpenRTB response. // pbsOrtbBid.bidTargets does not need to be filled out by the Bidder. It will be set later by the exchange. // pbsOrtbBid.bidVideo is optional but should be filled out by the Bidder if bidType is video. -// pbsOrtbBid.dealPriority will become "response.seatbid[i].bid.dealPriority" in the final OpenRTB response. +// pbsOrtbBid.dealPriority is optionally provided by adapters and used internally by the exchange to support deal targeted campaigns. type pbsOrtbBid struct { bid *openrtb.Bid bidType openrtb_ext.BidType From e96b980d391ae89ba1a9631f5921a7db6aa24ba8 Mon Sep 17 00:00:00 2001 From: bretg Date: Tue, 25 Aug 2020 12:43:06 -0400 Subject: [PATCH 177/318] moving docs to website repo (#1443) --- README.md | 14 +- docs/bidders/adtarget.md | 5 - docs/bidders/appnexus.md | 45 - docs/bidders/audienceNetwork.md | 8 - docs/bidders/avocet.md | 5 - docs/bidders/beachfront.md | 13 - docs/bidders/emx_digital.md | 10 - docs/bidders/kidoz.md | 9 - docs/bidders/openx.md | 65 -- docs/bidders/pubmatic.md | 33 - docs/bidders/pubnative.md | 62 -- docs/bidders/rubicon.md | 7 - docs/bidders/smaato.md | 42 - docs/bidders/smartAdserver.md | 59 -- docs/bidders/smartrtb.md | 39 - docs/bidders/sovrn.md | 3 - docs/bidders/tappx.md | 13 - ...Server Event Notifications - Tech Spec.pdf | Bin 89983 -> 0 bytes docs/developers/add-new-analytics-module.md | 33 - docs/developers/add-new-bidder.md | 117 --- docs/developers/cookie-syncs.md | 30 - docs/developers/currency-converter.md | 56 -- docs/developers/default-request.md | 44 - docs/developers/features.md | 12 + docs/developers/gdpr.md | 31 - docs/developers/stored-requests.md | 4 +- docs/endpoints.md | 1 + docs/endpoints/bidders/params.md | 24 - docs/endpoints/cookieSync.md | 55 -- docs/endpoints/currency_rates.md | 111 --- docs/endpoints/info/bidders.md | 23 - docs/endpoints/info/bidders/bidderName.md | 43 - docs/endpoints/openrtb2/amp.md | 127 --- docs/endpoints/openrtb2/auction.md | 789 ------------------ docs/endpoints/setuid.md | 26 - docs/endpoints/status.md | 9 - 36 files changed, 22 insertions(+), 1945 deletions(-) delete mode 100644 docs/bidders/adtarget.md delete mode 100644 docs/bidders/appnexus.md delete mode 100644 docs/bidders/audienceNetwork.md delete mode 100644 docs/bidders/avocet.md delete mode 100644 docs/bidders/beachfront.md delete mode 100644 docs/bidders/emx_digital.md delete mode 100644 docs/bidders/kidoz.md delete mode 100644 docs/bidders/openx.md delete mode 100644 docs/bidders/pubmatic.md delete mode 100644 docs/bidders/pubnative.md delete mode 100644 docs/bidders/rubicon.md delete mode 100644 docs/bidders/smaato.md delete mode 100644 docs/bidders/smartAdserver.md delete mode 100644 docs/bidders/smartrtb.md delete mode 100644 docs/bidders/sovrn.md delete mode 100644 docs/bidders/tappx.md delete mode 100644 docs/developers/Prebid Server Event Notifications - Tech Spec.pdf delete mode 100644 docs/developers/add-new-analytics-module.md delete mode 100644 docs/developers/add-new-bidder.md delete mode 100644 docs/developers/cookie-syncs.md delete mode 100644 docs/developers/currency-converter.md delete mode 100644 docs/developers/default-request.md create mode 100644 docs/developers/features.md delete mode 100644 docs/developers/gdpr.md create mode 100644 docs/endpoints.md delete mode 100644 docs/endpoints/bidders/params.md delete mode 100644 docs/endpoints/cookieSync.md delete mode 100644 docs/endpoints/currency_rates.md delete mode 100644 docs/endpoints/info/bidders.md delete mode 100644 docs/endpoints/info/bidders/bidderName.md delete mode 100644 docs/endpoints/openrtb2/amp.md delete mode 100644 docs/endpoints/openrtb2/auction.md delete mode 100644 docs/endpoints/setuid.md delete mode 100644 docs/endpoints/status.md diff --git a/README.md b/README.md index b69e7e76db4..673c2b1bdeb 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ It is managed by [Prebid.org](http://prebid.org/overview/what-is-prebid-org.html and upholds the principles from the [Prebid Code of Conduct](http://prebid.org/wrapper_code_of_conduct.html). This project does not support the same set of Bidders as Prebid.js, although there is overlap. -The current set can be found in the [adapters](./adapters) package. If you don't see the one you want, feel free to [contribute it](docs/developers/add-new-bidder.md). +The current set can be found in the [adapters](./adapters) package. If you don't see the one you want, feel free to [contribute it](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html). For more information, see: -- [What is Prebid?](http://prebid.org/overview/intro.html) -- [Getting started with Prebid Server](http://prebid.org/dev-docs/get-started-with-prebid-server.html) -- [Current Bidders](http://prebid.org/dev-docs/prebid-server-bidders.html) +- [What is Prebid?](https://prebid.org/overview/intro.html) +- [Prebid Server Overview](https://docs.prebid.org/prebid-server/overview/prebid-server-overview.html) +- [Current Bidders](http://prebid.org/dev-docs/pbs-bidders.html) ## Installation @@ -45,14 +45,12 @@ go build . ``` Load the landing page in your browser at `http://localhost:8000/`. -For the full API reference, see [docs/endpoints](docs/endpoints) +For the full API reference, see [the endpoint documentation](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html) ## Contributing -Want to [add an adapter](docs/developers/add-new-bidder.md)? Found a bug? Great! -This project is in its infancy, and many things can be improved. - +Want to [add an adapter](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html)? Found a bug? Great! Report bugs, request features, and suggest improvements [on Github](https://github.com/prebid/prebid-server/issues). diff --git a/docs/bidders/adtarget.md b/docs/bidders/adtarget.md deleted file mode 100644 index b658a728a2b..00000000000 --- a/docs/bidders/adtarget.md +++ /dev/null @@ -1,5 +0,0 @@ -# Adtarget bidder - -To use the Adtarget bidder you will need an aid from an exchange account on [https://console.adtarget.com.tr](adtarget.com.tr). - -For further information, please contact kamil@adtarget.com.tr \ No newline at end of file diff --git a/docs/bidders/appnexus.md b/docs/bidders/appnexus.md deleted file mode 100644 index e4032313f25..00000000000 --- a/docs/bidders/appnexus.md +++ /dev/null @@ -1,45 +0,0 @@ -# Appnexus Bidder - -## Using Keywords - -The `keywords` [bidder param](../../static/bidder-params/appnexus.json) will only work if -it's enabled for your Account with Appnexus. - -**This permission is _distinct_ from the keywords feature used by Prebid.js.** - -If you want to enable Appnexus keywords, contact your account manager. - -## Display Manager Version - -The AppNexus endpoint expects `imp.displaymanagerver` to be populated for mobile app sources -requests, however not all SDKs will populate this field. If the `imp.displaymanagerver` field -is not supplied for an `imp`, but `request.app.ext.prebid.source` -and `request.app.ext.prebid.version` are supplied, the adapter will fill in a value for -`diplaymanagerver`. It will concatenate the two `app` fields as `-` fo fill in -the empty `displaymanagerver` before sending the request to AppNexus. - -## Test Request - -The following test parameters can be used to verify that Prebid Server is working properly with the -Appnexus adapter. This example includes an `imp` object with an Appnexus test placement ID and sizes -that would match with the test creative. - -``` - "imp": [{ - "id": "some-impression-id", - "banner": { - "format": [{ - "w": 600, - "h": 500 - }, { - "w": 300, - "h": 600 - }] - }, - "ext": { - "appnexus": { - "placementId": 13144370 - } - } - }] -``` \ No newline at end of file diff --git a/docs/bidders/audienceNetwork.md b/docs/bidders/audienceNetwork.md deleted file mode 100644 index d55e8218a81..00000000000 --- a/docs/bidders/audienceNetwork.md +++ /dev/null @@ -1,8 +0,0 @@ -# Audience Network Bidder - -## Mobile Bids - -Audience Network will not bid on requests made from device simulators. -When testing for Mobile bids, you must make bid requests using a real device. - -**Note:** Audience Network is disabled by default. Please enable it in the app config if you wish to use it. Make sure you provide the partnerID for the auctions to run correctly. \ No newline at end of file diff --git a/docs/bidders/avocet.md b/docs/bidders/avocet.md deleted file mode 100644 index 6aa67391af4..00000000000 --- a/docs/bidders/avocet.md +++ /dev/null @@ -1,5 +0,0 @@ -# Avocet Bidder - -Please contact Avocet at info@avocet.io if you would like to get started selling inventory via the Avocet platform. - -**Note:** Avocet is disabled by default. Please enable it in the app config if you wish to use it. This can be done by setting `adapters.avocet.disabled` to `false` and by setting `adapters.avocet.endpoint` to a valid Avocet endpoint url. \ No newline at end of file diff --git a/docs/bidders/beachfront.md b/docs/bidders/beachfront.md deleted file mode 100644 index ecd7a8f95d1..00000000000 --- a/docs/bidders/beachfront.md +++ /dev/null @@ -1,13 +0,0 @@ -# Beachfront bidder - -To use the beachfront bidder you will need an appId (Exchange Id) from an exchange -account on [platform.beachfront.io](https://platform.beachfront.io). - -For further information, please contact adops@beachfront.com. - -As seen in the JSON response from \{your PBS server\}\/bidder\/params [(example)](https://prebid.adnxs.com/pbs/v1/bidders/params), the beachfront bidder can take either an "appId" parameter, or an "appIds" parameter. If the request is for one media type, the appId parameter should be used with the value of the Exchange Id on the Beachfront platform. - -The appIds parameter is for requesting a mix of banner and video. It has two parameters, "banner", and "video" for the appIds of two appropriately configured exchanges on the platform. The appIds parameter can be sent with just one of its two parameters and it will behave like the appId parameter. - -If the request includes an appId configured for a video response, the videoResponseType parameter can be defined as "nurl", "adm" or "both". These will apply to all video returned. If it is not defined, the response type will be a nurl. The definitions for "nurl" vs. "adm" are here: (https://github.com/mxmCherry/openrtb/blob/master/openrtb2/bid.go). - diff --git a/docs/bidders/emx_digital.md b/docs/bidders/emx_digital.md deleted file mode 100644 index 0ba81d59fea..00000000000 --- a/docs/bidders/emx_digital.md +++ /dev/null @@ -1,10 +0,0 @@ -# EMX Digital Bidder - -[EMX Digital](https://emxdigital.com/) supports the following parameters to be present in the `ext` object of impression requests: - -- "tagid" type string - Required. Unique inventory ID. -- "bidfloor" type string - Optional. The minimum acceptable bid for the unit, in CPM and USD. - -To use this bidder you will need an account and a valid tagid from our exchange. - -For further information, please contact your Account Manager or adops@emxdigital.com. diff --git a/docs/bidders/kidoz.md b/docs/bidders/kidoz.md deleted file mode 100644 index 433dd71c2ca..00000000000 --- a/docs/bidders/kidoz.md +++ /dev/null @@ -1,9 +0,0 @@ -# Kidoz Bidder - -Kidoz is exclusively for Mobile app COPPA compatible ads, 100% kid relevant and appropriate. - -In order for a company to receive bids from Kidoz, they must first open a publisher account at Kidoz.net -(https://accounts.kidoz.net/publishers/register) and accept the Kidoz Terms and Conditions and Privacy Policy. -Kidoz publishers must confirm that all of their content properties are COPPA and GDPR compliant and perform no monitoring -or tracking of U13 users in their operations. New publishers are provided a Publisher ID and AccessToken, this can also -be used to login to their dashboard at the Kidoz.net portal to monitor their account activity. diff --git a/docs/bidders/openx.md b/docs/bidders/openx.md deleted file mode 100644 index 54a0a5b1e72..00000000000 --- a/docs/bidders/openx.md +++ /dev/null @@ -1,65 +0,0 @@ -# OpenX Bidder - -OpenX supports the following parameters: - -| property | type | required? | description | example | -|----------|------|-----------|-------------|---------| -| unit | string | required | The ad unit id | "10092842" | -| delDomain | string | required\* | The delivery domain for the customer | "sademo-d.openx.net" | -| platform | uuid | required\* | The platform id for the customer | "a3aece0c-9e80-4316-8deb-faf804779bd1" | -| customFloor | number | optional | The minimum CPM price in USD | 1.50 - sets a $1.50 floor | -| customParams | object | optional | User-defined targeting key-value pairs | {key1: "v1", key2: ["v2","v3"]} | - -\* At least one of `delDomain` or `platform` parameters is required. - -If you have any questions regarding setting up, please reach out to your account manager or - - -## Test Request - -### App Impression Object -``` -{ - "id": "test-impression-id", - "banner": { - "format": [ - { - "w": 480, - "h": 300 - }, - { - "w": 480, - "h": 320 - } - ] - }, - "ext": { - "openx": { - "delDomain": "mobile-d.openx.net", - "unit": "541028953" - } - } -} -``` - - -### Web -``` -{ - "id": "div1", - "banner": { - "format": [ - { - "w": 728, - "h": 90 - } - ] - }, - "ext": { - "openx": { - "unit": "540949380", - "delDomain": "sademo-d.openx.net" - }, - } -} -``` diff --git a/docs/bidders/pubmatic.md b/docs/bidders/pubmatic.md deleted file mode 100644 index 610108b2e07..00000000000 --- a/docs/bidders/pubmatic.md +++ /dev/null @@ -1,33 +0,0 @@ -# PubMatic Bidder - -## Test Request - -The following test parameters can be used to verify that Prebid Server is working properly with the -PubMatic adapter. This example includes an `imp` object with an PubMatic test publisher ID, ad slot, -and sizes that would match with the test creative. - -``` -"imp":[ - { - "id":“"some-impression-id”, - "banner":{ - "format":[ - { - "w":300, - "h":250 - }, - { - "w":300, - "h":600 - } - ] - }, - "ext":{ - "pubmatic":{ - "publisherId":“156276”, - "adSlot":"pubmatic_test" - } - } - } - ] -``` \ No newline at end of file diff --git a/docs/bidders/pubnative.md b/docs/bidders/pubnative.md deleted file mode 100644 index a25cafe0cd5..00000000000 --- a/docs/bidders/pubnative.md +++ /dev/null @@ -1,62 +0,0 @@ -# Pubnative Bidder - -## Prerequisite -Before adding PubNative as a new bidder, there are 3 prerequisites: -- As a Publisher, you need to have Prebid Mobile SDK integrated. -- You need a configured Prebid Server (either self-hosted or hosted by 3rd party). -- You need to be integrated with Ad Server SDK (e.g. Mopub) or internal product which communicates with Prebid Mobile SDK. - -Please see [documentation](https://developers.pubnative.net/docs/prebid-adding-pubnative-as-a-bidder) for more info. - -## Configuration - -- bidder should be always set to "pubnative" (`imp.ext.pubnative`) -- zone_id (int) should be always set to 1, unless special use case agreed with our account manager. (`imp.ext.pubnative.zone_id`) -- app_auth_token (string) is unique per publisher app. Please contact our account manager to obtain yours. (`imp.ext.pubnative.app_auth_token`) - -An example is illustrated in a section below. - -## Testing - -Please consult with our Account Manager for testing. -We need to confirm that your ad request is correctly received by our system. - -The following test parameters can be used to verify that Prebid Server is working properly with the -Pubnative adapter. - -The following json can be used to do a request to prebid server for verifying its integration with Pubnative adapter. - -```json -{ - "id": "some-impression-id", - "site": { - "page": "https://good.site/url" - }, - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - } - ] - }, - "ext": { - "pubnative": { - "zone_id": 1, - "app_auth_token": "b620e282f3c74787beedda34336a4821" - } - } - } - ], - "device": { - "os": "android", - "h": 700, - "w": 375 - }, - "tmax": 500, - "test": 1 -} -``` \ No newline at end of file diff --git a/docs/bidders/rubicon.md b/docs/bidders/rubicon.md deleted file mode 100644 index ea376da427d..00000000000 --- a/docs/bidders/rubicon.md +++ /dev/null @@ -1,7 +0,0 @@ -# Rubicon Bidder - -Please contact your Rubicon Project account manager or globalsupport@rubiconproject.com to get set up with a login and cookie-sync URL to run your own Prebid Server. You will be given instructions, including the available endpoints. - -**Note:** Rubicon is disabled by default. Please enable it in the app config if you wish to use it. Make sure you provide the correct cookie-sync URL in order for cookie-syncs to work properly. - -[Rubicon Project Prebid.js test parameters](https://github.com/prebid/Prebid.js/blob/master/modules/rubiconBidAdapter.md) will work for server as well. diff --git a/docs/bidders/smaato.md b/docs/bidders/smaato.md deleted file mode 100644 index 881f8f2ab54..00000000000 --- a/docs/bidders/smaato.md +++ /dev/null @@ -1,42 +0,0 @@ - -# Smaato Bidder - -``` -Module Name: Smaato Bidder Adapter -Module Type: Bidder Adapter -Maintainer: prebid@smaato.com -``` - -### Description - -Please contact Smaato Support or prebid@smaato.com to get set up with a publisherId and adspaceId. - -### Test Parameters: - -Following example includes sample `imp` object with publisherId and adSlot which can be used to test Smaato Adapter - -``` -"imp":[ - { - "id":“1C86242D-9535-47D6-9576-7B1FE87F282C, - "banner":{ - "format":[ - { - "w":300, - "h":50 - }, - { - "w":300, - "h":250 - } - ] - }, - "ext":{ - "smaato":{ - "publisherId":"100042525", - "adspaceId":"130563103" - } - } - } - ] -``` diff --git a/docs/bidders/smartAdserver.md b/docs/bidders/smartAdserver.md deleted file mode 100644 index 4d2663f8a3b..00000000000 --- a/docs/bidders/smartAdserver.md +++ /dev/null @@ -1,59 +0,0 @@ -# Smart Adserver Bidder - -## Parameters -The `ext.smartadserver` object of impression bid requests supports the following parameters : -- "networkId" - Required. The network identifier you have been provided with. -- "siteId" - Optional. The site identifier from your campaign configuration. -- "pageId" - Optional. The page identifier from your campaign configuration. -- "formatId" - Optional. The format identifier from your campaign configuration. - -The network identifier is provided by your Account Manager. -**Note:** The site, page and format identifiers have to all be provided or all empty. - -## Examples - -Without site/page/format : -``` - "imp": [{ - "id": "some-impression-id", - "banner": { - "format": [{ - "w": 600, - "h": 500 - }, { - "w": 300, - "h": 600 - }] - }, - "ext": { - "smartadserver": { - "networkId": 73 - } - } - }] -``` - -With site/page/format : - -``` - "imp": [{ - "id": "some-impression-id", - "banner": { - "format": [{ - "w": 600, - "h": 500 - }, { - "w": 300, - "h": 600 - }] - }, - "ext": { - "smartadserver": { - "networkId": 73 - "siteId": 1, - "pageId": 2, - "formatId": 3 - } - } - }] -``` \ No newline at end of file diff --git a/docs/bidders/smartrtb.md b/docs/bidders/smartrtb.md deleted file mode 100644 index ffa88f663e8..00000000000 --- a/docs/bidders/smartrtb.md +++ /dev/null @@ -1,39 +0,0 @@ -# SmartRTB Bidder - -[SmartRTB](https://smrtb.com/) supports the following parameters to be present in the `ext` object of impression requests: - -- "pub_id" type string - Required. Publisher ID assigned to you. -- "zone_id" type string - Optional. Enables mapping for further settings and reporting in the Marketplace UI. -- "force_bid" type bool - Optional. If zone ID is mapped, this may be set to always return fake sample bids (banner, video) - -Please contact us to create a new Smart RTB Marketplace account, and for any assistance in configuration. -You may email info@smrtb.com for inquiries. - -## Test Request - -This sample request is our global test placement and should always return a branded banner bid. - -``` - { - "id": "abc", - "site": { - "page": "prebid.org" - }, - "imp": [{ - "id": "test", - "banner": { - "format": [{ - "w": 300, - "h": 250 - }] - }, - "ext": { - "smartrtb": { - "pub_id": "test", - "zone_id": "N4zTDq3PPEHBIODv7cXK", - "force_bid": true - } - } - }] - } -``` diff --git a/docs/bidders/sovrn.md b/docs/bidders/sovrn.md deleted file mode 100644 index bc6d42333e8..00000000000 --- a/docs/bidders/sovrn.md +++ /dev/null @@ -1,3 +0,0 @@ -Sovrn supports 2 parameters to be present in the `ext` object of impressions sent to it: -- tagid: a string containing the sovrn-specific id(s) for the publisher's ad tag(s) they would like to bid with. This is a required field -- bidfloor: The minimum acceptable bid, in CPM, using US Dollars. This is an optional field. \ No newline at end of file diff --git a/docs/bidders/tappx.md b/docs/bidders/tappx.md deleted file mode 100644 index f92e1cd4fe8..00000000000 --- a/docs/bidders/tappx.md +++ /dev/null @@ -1,13 +0,0 @@ -# Tappx Bidder - -## Parameters -Please contact [Tappx](../../static/bidder-info/tappx.yaml) to get set up. Our operations team will provide the 3 required parameters: -- Tappx Key -- Tappx Publisher Endpoint -- Tappx Host URL - -**Note:** The Tappx prebid bidder only supports in app traffic at the moment - -As for test parameters, use the official test parameter specified in the oRTB standard (https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/OpenRTB%20v3.0%20FINAL.md#object_request) - -For any additional information contact tappx@tappx.com diff --git a/docs/developers/Prebid Server Event Notifications - Tech Spec.pdf b/docs/developers/Prebid Server Event Notifications - Tech Spec.pdf deleted file mode 100644 index c0bcca753fdf8810619f620f2e24dff43fe346e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89983 zcmd41WpHIXkS1s-Gc()QY?qmt*=1&CyUfhY%*@QpP-SLjW@cvg`1R|V>5ZP){kOX} z6w;LZI~ofc8`>Cs zOa0r*>Dya50RC};kd392m9+za1wbcfZ){{{=xAdPU|{?<4M3;*?Foi&dl~-mgM_t_ zvFrB$D}b5p-@b_rfRW*Q8wvnA1sfYj0QK8X6es zZU5FY(9>gwB1+Tjv4lVzX+@P<^b^k}05D!)h8m@V5`BR7)Gq)O>^W1b;7PEE5qwwh zV5K6z5G;J94fSxtNq`^-VG{p|>wl{K|0u%$&1e7r8-V^N;{TIP!p07c4ghB6e?cIp z|J(Sx$MkRSUw{hfJL+57{QfVj;rK7}|Bq-@1h9Ss=|3^e&i;Q5ww~TUkt?jH^Swx%%AQ~JIATdm}x`Zj>) zwlsj^2_8K7K{S#k9S#;2h~*TC>%Ry7oBsdXN&#zYn{P8U0gV3;PyxXBFX$Qnu|xsD z^pEIw^Diry82|14>jb8MJ;TKE@8Ca)=v(q%ElmGvVft4K^S@e{|JB0$ua<8H3fWjY z8e4y_{+);ZnM^1B%~yRvn}1mO&lJ{g+Olyl{zJ}het%2;FaIkTJJ>kc8-7RRAIgik zI*KVee&>Y$=!r1_82?@3AIgh- z9CjZVc?~g?aPmz^vO{v?^)vZX$0Rgnic#U0^3R3pc;TUwB{l({R={7M_qPK+y1D`A z6}I=Av9YbM`45?@@wCsi6Ps5h$D5@s@5^AH_nWmb1ip&Pmaj)9SADLYs3^tv*IW^( zvykMj*&5V8db7A<-CFc+wIAm_x4+2=O5EREUv{)zB$+J!#^~zy*W6I%gI-0dgFI-0%wfH* zXq$E!{3S6o{-rZN9vZVxYJ|#mpWuX&(K;My_xt!slSHxb;<=%C>k4UsB5uAU-e!-D z@N2Pk=Qr_h`>jsyKG2b>2VU)cB#k3}u!}mV`y4u8@{3Ptf=1Vd#&Dn7>XpZpr`wVHGlj(f>$f-AKb8K0Cf1RM z&=^A6v(L^Q*RpNOJ=yeG*M+wSw;z) zfDGa!NjJaFLXBI3#+&E8o`HMghFiP4X&2L#*m--y{e#E5_~)(I$u#QsSXUx$9 z1$dz_N|?6}T}|P1koi(c_;!jrZ`I|B84W_n*J=YpfZ&wJIiAVhE6uW0k+y6 z=R~T2M26=@F3pM5yDEH5JJKJI?i)QMnL^Pwj-vU?dUt;;-Yl5{^_$`cbfW}R2Zr|G z+m$I-h|j!v^=;)u2-$L2kdfJxY>Tx)GL!g|B(6Ak^KkGysv|;h-Q_5HtdeUxyG6@eS@=;W>?LrWfP^oSLLW27SvR*G5MP}Q2`YC6cbQEP}!vE>_@P3h-vMEMW9~x zdt$6ckSeo@@D}}TT$RdN?^>@B$b%HFGG>8ZOkjf-K~k2Y4po!!N8kxcstvvB|%3^l78=Yp=4s)R(s3=vv9tv*BL2gzlb#GvDEy&KzE zGM$I>-fFHyP&@~xhd)B%(|Jg+DCuI5^E>I{G-&b?_Bi-*?n}k!N3ErQp!%ELM*02W zGjaG|;83Ucx_MAQ2+n!0$0;Re4N7?evWk~rHJmNTYEJ=JK_eWdQIC)ZkTnv6B!~bP z{y+IF+bHz&!1_m)4G%S};$=%C9k=1mQ0LCkw7W4T!Tz4HRdi8Xc}hy8NAmVWpd0Do zf$EC6FsoR#KSMH&V_<=_i#Y3}P2$AnO{J&}B0qB)R=M1^#pj+koB^6kkF6x(@d><}1T?HJ-Z4>z@nr6*-pXTLTGSG3WR$FvY0lYIk74vUx zxKqOIS&%W!$NGG|_Fy?FPE!^K5gtzS*sx}Jw<^jV+*Z<}q;3^@a?`!T9m^hCa(dM2 zByQZH#UK@R6QrjZR^Z$Sg_kI}$*v}4*8GEt)m3`wb|J<(?w-=RQAJlQObwTh%>$2q z(Eg@!GtKozg=M%MX{J02RVH_-`;xaZiPyoRB&C{21TWk=Jh%eC>w#S7^E{~k`uTeBjK|UqVC_}= zQUJ}sDQL4m_;-M*!>yJ|V-swxj&NBM5eV9nKUe+vu|m_a**z960JymWP2D05=EH(J zRR?7Vo}Ig={x6|>%})hf{;gWE4^i{iuKv2nYxJPe2Hk75shT2(t)tjKt}BnCtRm z6;M);K~mDDv0OJEF7A(Wm2o7u6^^{UHH2?;@TaY}rZ29s=>T&rrM=**mvd4hW-f_( zO@em=ed%j}+Gr}C5AzNx=R(PR%m%+T`JglnDmK8eij04Yt36ECL2ho4wKGeGmDd?u zn+fFInAbuQ^@P@()?&bAeE#FmfCAeJ@X&&zZGiO5HnZ9$v3*$I?tzjy54em=Pj>zn zm|y)6Bav;?Ts_bhReu`{)z}*Z&O32$WF*Hf{cW-R`FO+gaS=7sUu`$~$#c$6M;{SoScQ)XSrY#4Ze2?sB?uf0(<{9fN_)~= zQtlYA^s63K<;*2lgXxjLrDEVYBdpT$b0Oy!vcIEIv&_mIrGFSJvbuL<&@8R=K1QZ7 z=zkbdiH+h(RF&ByDx!hl|Ef~vuJG@iDu>%~@RdtqpXhOOj#7kEoG4YPX|u0;Ne3;4 zxLunG!ZW>$`v7*fBpJ+7&^NujQ4SDcy{rU0MdtuL#A6Qtik~n`(t!pV69m@EIXg}W zW9%nOnFV|CXP^(JBWi!6D7t1V+{rvbyH_&>)uc4>I!u46V0d1@c0>UElD_WPc)b0P z^{HtsDkmU>A!rP7p)5b{=>zd-9ADy7K-vn_!h9@lF&4+j9Y4kP-;UegXPN(f>iWNV?`Hrov$B7m4FBgLk&%Vt``+fiUJ9IdxOgflKhd>6 z^1ex3rNv8;DKe%(QN~I_3o|DAGM~f({H8M?2~kB70Sj_gkf92~MUth#zW&1n{zR;D zz`$szFueuC1te*1OT0`1+h$!JTOMC+1SYpU7E29J>K;vAlM*05fac|(KnfA?*j#&u zwY5`#)H;DeHMku|NuKJVa-@Jwk>CKo2pIJDTg$?5d?3WJ!2l{Tp4&S}djwtY0RF2S zU`~=4Y+l#pnfJ3_H<52h90j%A%#*E9b4!R&1%uM=$H#xdfq+Ip>1*8d-luzv^u|xNZ5^UDvN$hKE6Xl~ZFDWK!8Zdu27px`4&|+RK5T*H4D{ z_!ysh!v$Xbx6taKw{x7f$jBW2yk+BEZ7eU+&rS=@fHg>3$`*pVCbmFtLRi7s8fwyy zO%o}2-x0I!kQ$(HqxQ-n`uPLT=E4EZZMTBRsTaabbcLC}a=;p)=?OR{4)rM2(V-@| zP3KtqpWq7O{#Zs&4Ygv^?_`la1 z`F=uUL}I)JW81bH@BXC+xgjcEsISlR!#Qt|YKBTSeEC^P5N=FI zL^JrVA%)QGeG=EG&vOuGl(@~oMR>T2xe?ba<~YbI zFTMsijC0ZFbz^IUKa1<7#`VFc6Kjl^_PF78sO?02Bh~XpFTP+$z*6Ap4KNjK;pb)P z^DZ=V{rKozigLvey|L(YlV<-K&iusM;c)ImV?V@!q?Fm;~jogJ+BmWeHvqY;zsusXs zlun@OJHrfqpXApHVSZ&|PI}P;A64bz{*vmCaGm@W&kY-}$?nZmsD7SLBIJMsV^>KP z<+el+CWfvxIrR1v3M@RD^s3l!FkRGw3$ClywhxXt)NmYyt$4yhgI!sL?M8NuD(Cc%i)8H`c%0DCsRI^6cmj=zGAiQ^ZNlW+!;qMkYS zIOyPzwaYjsPWpmBc98v^rAwYtmVxRN^YH`k8J7f4c*VR>>&kXgiy(Q1m!9B6GI38` z%jD%9oAL-Ig(Zx<6a`7ccC4xR7pK1x(6y+MmkP1f{(v+m^x_`Jn32$QaiyQf3Q4P~ zD{^pc#k7pLzli~7^3H?an)xfry%KI9wbqz*s;(V`_9P*nz+foE4JR8hpKLeG@SMG~ z|6l`(Fe+P!H=-Xd65HHR;XU6BmjHb58T&zN)1^<-yAQ;DVJnu^;o)@5ZkQZo%@Jia z%59D~XYYo1ohL*-a$Kc2;TW2sL{vKhrqS^l;+{H?Mkw&GF@&8Lwds*OMs0h{GYc*v zdJoFE@bNDNsHFQJ<4Kx&^xDC!a6|dz`sfgRe6vt3!!uTlE+y0~v!ZSt3HBKTag-Pe zSf^VdNLr!WoVzZG1u5dLky_R{q+h~X_BcaUBrOi>lg!Os37fNB;nsp2htAKK_c6y#ob&>Hl# z`m?K2&L?WFBlpGLB<~bQn|omE;Fs{Ma+$YSknn`PciV^R8TVf#)xJSPJTBzbZ{H6z ziDE_rl;5Vi%`5MU{xa@0LzGIIIq2&5GyzhuPY0bxF!>Rx6wI5Xs6+M`;8dRjNO%y? z^gs;0Y1&=tIqF-|VZdsHhFJ|miS1!Ez=0{P-M|im0CrW5huL(Y9uygdd6A1P7~c%B zdVX1y^a9Zfzc%dXhbiM5B+}|+r@PK%gt<@L4a?DMT84PdgRe?b?bT2xnCF18>~?66 z{CK@?)8qTQUy~E`Cf{{fV3i)j3PJL6MNp2yy7#~x=!{3GL=?j+)TkE5jZ3BN!G+#j zch~v}nbAFEEc{5ls*f+r4WmpA7H(vykCaSl*(JTA#9A9^qMYWFb2Vf>Wi9=@``GIpk!m*(}&^( z9jU`n@4KOcNe7y@e`;fD-rnv_XQj-^(s$v$Hh`S-5#SpIkCxelech_^U*(sw=G zUYlQbwQMFE?4rL6yl0(m?+o}hgLx2Ie?w*oJohp$2hjRYgtFYDHlqFM?K1+!PF0@H z))KA^*P~jenz;zYKi|E^y2Jd-5;7Oc;d27w z&puF(fi#A6#kRNs&AZt3vkT3DdOdp!E(v#EqttJ2@^SbG7nd$%Bj8;)mzwq^`-}%5*s(-0&4goxSKDXL&>oK785S zCwqXqOw$wCPTj)|dM&hEdVS8}m&EP5gWG_ep_q54D)jA{;*Z@vtA`U1E3I}(*ilEr z=vfzXJ0IRq#bmj?hda{DzA(zC%Mx@>9o>P96p7o4Lj#Xyr}ib5W2Q2OYy}*&2oNIR z6Eh7X-=eDjDj&f@&L_Rsb`SI>MHE0x^jCG2Dn&+zwg^H<3wAODT?RZddqj71>lkpY z@II2h39M6f(tJ|rB$yf1F{V@oRhC#*T4q*iJ*HY#TeewtSQc1joP(}Ft>jE{O!95G zw+vi34*mVOG`VziN&Xx3w|irBW0Gf+r>|r4que7x>|WTdGTSv~Yi@jQjcNL>^0yFo zRLa2Fp7c$!t4BK(FC{PO=jLfPQ}B;6W(LT71!n4>{C`(H^FtwP0CBitJw394l_Iu6 z;O#GAfUWJVS?#Usu91nQhTLoYEGabyRqCD2SycKu^-}uH?Q)*uTv>WKVyFRBZz-97 zPb(d(?JU;ituxl5GJlCq8vnT?&cq4G^nl`MIbofAxLv;d-}6(| zKJDI)Ug+H|ML=7bP+JRpd=NM1OHVmDUY}DnY?(RXZA(a|s#B_2KKExMM}wIj&z=`8 zWk(n}Yqr)aE5^T+w{I;Jo%97wq za3uSbI>)-hvVTpP7eux`SkZpdUtz~e1U$ke?fXtdC_<=zt`m()(C6mLJPLvAmwP` zE6LpoveW!5*RvXj>D%V>41AlOeLqVd2*qF^`V;eDWGo)Ct}zBqBpP^g#aqb*afUUr z@sx9DCnn2hVb7FxO`uu+X(gSqRu9ttqd`BK9x=Jcg&qUF55yIiwbvaMsXVr-VcV<~ z+9yQh8h1QI$QHc1JE9iKJ3w~_yE1_08owP>Qy&sGsE8gmy4ThfBf2}}n%5I(Xxjr5 zn4r(@8B!-8?iz+0DyN71nsW3M;TnY-*ier-GRTgi1ECii6XAd!m`o5%Dp-PIPH_jB zJ8<@co*N`4KeG$oLLZzPJc%B~J1|F|PF*ycI1w@s3F$O9o0^vtwIbl$ zOJM7-6bEL6u_FB#yCeZOgN(QG%RDR_Gc+!Bo7+=5KQ!%}_$@3*ZA9=E+${kY05PJu zk60IHx(vQR61+UJwoe~M3Xd$7xo@+N_l#oQ&2~*=hs1Uh>v~J;7K2MDHuAO)+L%Uw zOeX>0-6bCy5!*Msg?dT!3`P(3foEXT(06Wgj`v9G5rd~Mw(@ok>Ky4c9NiGJJamroNck3-rANMUbX?SFRI+U5 z9PBkPS?_Sp{g&?4*J+HgEb@r=maT2TS4*%Wd;Z%w?W4E4w&MKiT=fy>@1QsNRy@o( z=NrUZz{iMnE$1>kmnqsv@J21f=HSMR%raQWj-4yaVIIvX**)qX7@k00LEXF@IqG|4 z_r9xr5BFFe@Hc%hEzt>2tZbs7+Q3FK#o>fwx@FvQ5i7{|!?KdRFK*4yhUpj#_3D!< zwPGvg&B0Y7vLEr&TEj@*D9`xv;M32!sdC)YT;uR6&{BybXp%`91q&x|qKb(t22CPE z%Z7M%GAU~)SJKxnC>bQAC55b3$;sJqX-OO0+hZs*qb&>J1s?huPcWAm7tgYMzu^5uCN3>1zi7W3X; zH;NVWy)H+Nro~L9^ZdSSf00$9QEN55i7%4neYj_uqV=$T2~V1;ao-tOuvo12F#8y* zY5$|YS3J?1TvcOLvDs;ORK4M$Q?sJ^*X9ey2A}mti#i_8B010mYW^_}?n zIYE|>%kK7$u9Ms8bzJrL)J7EE)nK%X;n3f@y^ZA?>vLB&=XM;f%_zLD-*tw$PG|i3 z6&}zU&BIe;R@qreX>r+$m1gFW_6iBYU35J-S9X6h?V{B@q#7s88N|{e!8>Fsx}G*YGH%;l=+R+2i*hShqeL!jY!okO6VlskY_PhMi7!pD zAX{M9EhS(tK`2bqPib%eRKzsh{jm`L!XHT0nV;0$t?r~Q`YU--q@WRD76ZZ68j&y7 zxNkv3jT2(sW3=K^q zQ@(m>rK*ItXEpR&j^;TW9HHWJv!Jz9yK+czmbodt5$_aF0dA9Z;T5$Esxc|JEV-#F z`Bz`7*j-chBM)74R!YVe6#Cj zRa2dyvi!X%+mwp(goA3$iM*#pRS3Z^$!px@l-Jc1<))!L3?dY+C;L59M;Qq_mb3t& zKQBqw70|j{wl+v~lsJ3UD(-8pV#-mJeMNPu%0g2iJY46G-J8>HwHR#`?ie0C3qf_8 z@jVx~YZGz{cRN`Oz%Jt#EYgZCIFb5;)=MYEP*IPI{nr^z3X=#$~>^DMvhJOR_aN4V01?x6(R(Kp~_taR&;kd zpD&N`J#olsQ`cPABq<3Q`N0oSC#Q*tr~5{v#xc9Ln{Yw~F8lzRqQdm#oAiJvmSo=c;`+KnlP5+x z$y-{prQc;L&o-aOaEqn+@-*dP4}3}p2um)P2N&xFK6QUf%c*XTu~ zt7uOV{yarbGjWPYT)U&O8QWszkxueOG4Wn)bNMR&xnSi)VZH;*B?>9YeNX$?x~3vT zFfT>$IUfjrXqVWp0ugI$Qj#TT5x z8BLIEW5e(mY!)cRVP*Q}x5g^=<+CREmR1APAH+X`pj=7r>5rj+F)ER$<)U)^sTGhB zrMOVu(<%@kVd-%ZA%lymrv?1Zr?HtdD@;RmfKghH>F(`YgLO8`m`tjzH^RI@*AO8P zK|a8{bmwaH!Fgh^bB7EG4On)8cG-8K1x2U!o^ZXVW*e4IslMYT4Pi5*Vb!?4AO@?7=b4C@~(MpfbgKRGEu8ytbD#k0N174U(N(5#;7&@m`~e z%DkNOXQmNlp{|V+8M`X8EE&E4^B)5}ynP8i;MT$}!dM0%g2818shm(rp+%uAq2__G zZ<+nP{ScuEvC)u#Kg6i_1$gXIa6FxTTZ0R$l6-|%{%~pTk)7l-2*+H=UOAuA z`C_r0ari}7=eB=xo3PJ--!NmbV~h1W#J5({^z4H)a87`YVqjv-LIs7{`T_mOfsy#R zH(bGOQm*k{wo1Uu_7LxY8CAV35D-HxU?0sy1~$HD&Gi6E?n>vH$2ni073lHX0G}Jh z)Ln?L^Pqp~#ij<;;zv;xP5?{P-Wv8_5$0!U>}Vh4xfsm4GB~g%jc=sKQxkdr+47h62N6;c$}r29_nN z!l%a3`8`~oVR?k`CWwVkAp96&K?PdhsP&tjK?WI_Jr^>J;JXP8qi7p1Ixifq&Zx0# z)=VLKt=ty*$j!lo_9*h{y*fk&NIOJ3FeJg@S|@H9&oIaNJq9@WP|UzozgGWPiMcla zTzX!{0jt^mS)zo?L5F**_rbj-As#>WfWBCRR#mlKfIIogAuNX(t=bFimXQ*sT}$&3 z!7ATp9+z4FbKFX~m9G=KjO^{#**EkS(o;A>M+p|mQ5ZoN=R(V+S_mG2Qoe)Ns+(*Fb}{k$ zN|=Ejh#!1UDr0iI+$iRx zD{_e9o^Rk}D{^RRfBP6%gHqS>FIsT)h7-6_6%uSWm7zvZegYvJ|9d9kK|6SX>#MVEf1+=9X%esYeeKO>u zP8)=LS*Gkq&irJFlH>*E&PB5-32M_?b}XQ&t}Rc{Tv6X)YxS@cVeQ#d0mp(AH)$2O zN#&Mxp{kG!9)i`5vyiMG9=&yK6VRPF*K_)9s47Fdw8f}Yn@iNG|dbdn= z;PZ;LNk$rUZmG`b2($%}=oS%7JX($+g@vZA&C!3c$=gU$FL0d#hs7B_*V9+=tG#bc z8R4PX!N!JTTUQBTdx56oqPfpEe{*0B;zw}H+6?1#{V-Rn_Q)jsMuUS5&RJN7p|Q&s z2Km{fZ1Tj=Y3v(axr3V%1g{D$0aZ%O0^zg(HVg^Yl}@`hu$3hJdE&85xk_Gp@_@VK zsu~=MhD7h$EXs$PCCwh)V;KYV(sr~=#I~}uCY#kng%Q83g0JFHPyhkW@oenAWl{Cr&BHMP`gX# zHr!uh&!oyNrqqfR5%lY@7_5E0=`DbR&D4?vtC>h-4bL1 zB4KhD=j$2yl*f2s=x3uD6v9mMB?ZLyypSPf?P#eepONFZTf25JAq zUa`9i@S0W%YeEdDji?cXy%S>QKlbP+l?f;iN^#{Wxfiq4LILguUzQZ@Q(*@R8CKqQ zd3@}p-A0n-QUjaY!AcF$spL!M2Ije;GI(JkA!~%0)uVZ>YqEu1dkh%LMXw~`yayfk z<>)P0wtzP!#McmV!9<70^tSQ#T4kG_9lljO{kl_zaHGxv(zs$E6z5XLN;@AFQQLGVcQS; z4j(Wxe0AXZ*g(PJQ5uYop<|Gto<&XT%3IincGGuv4bm>s=vrj0Ab?L!KGM=igD0F* zvIRL+8+DHIqSt=vM-PwQWAY5a5XU_mur4bacVdL^7GDV%8W!6}wvzUktGf@Um!!bh z)U&!?i%_mr-_^47J6EV>cV&OJyC%pRpVm6|yQN3@{YBj#s@jYZ-Iwyr1F?k|R~iQ2 z^P9epT8jcvGVq4I!mU@~{Sv*?#MwDBD0V7sm(i}NRbZVOe|KtbKc?WMEt zA{dh_GcBttMIT*Y4Kzmnj(H6KI{3Bkv7bXZ`mC+A3|F)DS{jgvVFE4EYpo{Lpay%X z#w+ZG|D!Lk8kIE38gO?~V1yx!ND$vtNHgMPi`McuHSKb2?w-|*`X_#}5F>J@t8A2y zk!i2%MZ0@5c+8SdXD!6_FvJM^7PEtBt{a}{W0UzMBX&|D@M$N}HhOqPgMC7&UzB!E zHRzH8%%)bwfWb9T`8r)r20v({5t3iIL@m_blVBy#Igf3G#0Qcv()~(^aulsINS!{} z8#qNi=BUrX2onb?f-piVBLiBtb#H z=p6b3nXJ#Q*uS9{J@dovHGMis8_gqLz7#m7SBnB+A~0mY;1LQ73giTa*BQTR&5f)U7S6@UIxqY<^ARj>^H0fD8+SxzvN0cbuRfx*D&@$GjhruwSgh>@~%+sdE* z#OqYJftA}{jvl2Q^uRSwxc879*!Do0 ziar91D0R3uSibFeK%*2Cbrt1`n5QA1aJ+9Y>CXD54!zSfpwl%V^z$H?OFPNrXm#L# zQ%+wuLSND=^l^w<{jK9q{r+p+rasi7#K>zUvE+>w+oE zd{A+7(AY@B;&CwXft!r{Bgt#m0#ih_&-Cj|dwt-$eBm6&dSbZP{kL+{1uIzyK}+OQp$l*2 zEN?GBR+C2sA`hsC0V&--2l2xUl4|D<`d|i4Gy*dNpP>vs1A_y5!m37dw4(48l)^c^ ziH*BtaF68WM%0tLLWf_3c>eWh?Hn63%`l%RGNK{GXvB~ILcN`FNkYxTLQY=QQ4&+) zp7mOa`yz!4moKE1NE@FLT~MaY%Zr5tKnl>6XSxeUaRbIMUyFR_Q`G(PMe)a+JSIcE%8?SU3;RNW< zh5-$;hBOiJYQi?I(4Q#3Tu8kn8>2PiL!?u#Om%i@pzvTzrCv@xeYw@)$r-Aup^p}yPV`!GsYYr5m#F_2kqT-6vsKHRGt!!uNnPMxomN z-k+MDa&2V>BXjJO-rZ?+2KjeXtypM|{p@a!Ick(D6sx&(>0cQZUZfXPa?}wXBa%7d z8A(FFIZQl`X{jh1;2z~FNcHeQd&!O{sj6(1`e#NS!|EIvxebbxo7L+a3l3PQvJNWk z=YB3wLwId{$Pn?E@Sy&(xVTC^InLCe!n4B376~bd>r@A|H+`%8q+MNDB}*<|yVIU0 z$k;BPZY*msV@iTHs9I!?STlF}YoRPX@l#pRC*IG9oI?}xbh*uL)6ZBQC4W&E%1qvm z0^5nqT7o&OI`0ECI#p#d?2;WhB_)N~HY(Y4$q3eQEX-5N`o+GwsVJo;$)57B?S(xh zvXriGFH8yyox{uwk2yQ_659BjodHEGGS*4qeGVUD9&g$>PEP1pX2KW-7dh3OIYR^q z`4U;e9UA%Ut@~b46Pd8aUFA6Lj@k(MTxT|q^O}Xw8m^E2ir==Z@Fuf~uKUzZ5<(0N zCpiY(>9k$#(yu?@Puwg9H+wwI)_9zbFZXD7vQO!)trO=h7AD^BQnns9g=7#VQyo4M z9{MsfrM;5@;LQfT)d>n|JJTMbrETQEXJV1|o+?VAT zda-oLUcODm{lb5T(VPSIwqU0|Z-^3QIpPOh^Z2y3W53c6i}OWyo7UZGP*W*q#1Yup4+ z@*;EC{Q2g@7EHxJA9rR4P$xh0K>`h=tQ}4*i&|H36&#EsUCmOpS(2?k?vbm9L~%1OGSMe4 z)|$qGud&BbGKvy;u@{39QDH`ijP_>r>>{7<;F!L$j(GE~(&V{A&u}6Bx{RzdfQv~V zpTeK2bYXEKs02dgiEXh2jvUUsoW8sgbIMbyip}Mh(k9C=SQ&bV!PUZso?vmQ@T%}I zp8}69r`9iroG7iJJ}`f9uh_pZ_Qf45-WQ%1zBX=JyD-k2&zoDJjrtB{WL1;oGE65+OVE1|*jEa%IQj+8&ja+Xqo}Sb@R-(uB^f8AP7ICx%X39c=uO0kSCng~EC%S8%>Ynf5Y_$#J&X(aOWsW~+|g=Bt#CS7HuB{iI*)l^ zk7d;8_K>1Xz0q3rOxA<%NqUv!QM}@$dWjPkum4EZ_l#!fg=6Q9U*=a`k!6l6f3xgo z^=_!pro3YQNfk~7R&j<&I{HJPS=UMahrzk>-$UQm2MTNpTg+WpnSk?&Y30~G5USXp zLaLz7C8N2qBhj4aCLw**w#)K|sLmm|PT2IgD=QP?kt`(cEsJ4!Aq~NoFvm~}r9Ulm zc0Hfn!35)bo?j0j8RqCHqdC2g5wxJ&taf>SZo{Iod_JAP8qicXqF002OAVyU zrzfRHr4wVRhPeKHxN;+Z)3^^o{0M8#qf+&tI%=k&%O?0Pwa z*{P!8(!@lI;o>XUcuT#0$BypQskX!%z8ZCCAu@?NB+3YI)o>m@@XJyqtt-O@m}?iywtfmE$*pt12VPALPTf)HG{c;F*tF2yI14v=jBe0UVvSk`79M@7F&`L zGc-iC2M-o5Kx&jQ+3m-4)9gMAdqQnhs`h_&b@3b83-p_WPzJR!M1}?3D5W)Q_w?p= z7ibCf*X_nr4A@VtE;!D(oukFHdWn+*Gekj#=hmFjB+axdDJrm^q`w8T(_lh=JgCai zsAF`u8Ry&l%H;o2AK-A-aEOeQp^rLQ%{Lw$6Viy(>2M5sSS};brPO3Q>69@>_>r@R z3(vA$UG~FR8=i9)7FL?65J&W;eC6!Vl>7mT)Y&`Y6e4Kv>$A9sdYiKl;O}NQr%JGfxgJ7 z=>#C}7L8}`m|QIZ*Z%^{&m|60J0PMHN3Vdw9-1?YHd$^0Is zI#Bv-{ME-U*>HE_c zaulW?PJN|Sb-B>Cx>DeCLtBi2daKdskN6spk%7!u*FGO{IZNdlND;Fwn>YfN{=h)< zY3|T>XVFFCqdUX98ArOfX)5~&u{QH*w6a+ifu6fgD-`w${9+*-lMc%Sx`AbJb#aEb zQzbX4GAg=89)XgGcB;Nh-9$Z8j+&2>khREO#5lSyB_ksVB9@8Lwr|fFl^gmv=Kl7M z?O~odN8^?>o0tqrq95;EyYyGbnmG?Lk)-o%RF>&*4-6%NW>>sE68}D0%lBDctfM~x zi#6Aqu3W@KZJxd(@|CS!m%(5^ll0@~mIkJ0`Z^O+jpET`Ocy4%^dQ`Q%f(jyQv*zZ z=D=!vM;}CId{S$d{r+P;Y+lI>XTHVIjf9iMetLV|g$HkDkJnu2^wTctdw4#!+Fx(gkHoErPnwzXzr&dZ2cSmk$p(!=))oZT zme!|c*-R4ISuc_1K;|U%gRF}TLDgm1FlCSPyJ4LJR`h;z^RLd3Bnn2?=aG`2JOBny6SrhMjFRQQ zOhuLhdm%$aLvHaU!`H3V4!MNt^t!RXzB_7(YYv%=oll0Shv(_ISp(kd%y^q+OEl3c z^N8Sn9ub-S{r2*dQSD}~eLb4+=Fg{eAy#ombgJi*h{SXI+qwJ7Ij%YUx)%pdhKIs7 zUoyY(?=Ebdlgnq*<$B_})_tVoI>ixWoqdoL;m=h65rh1T8Kq(sGbN2AK!e)EB1Hut z0?iWNV0MR4W9J&NV)WP#wG%>Sa7u~@3hL5# zt(eEnQpT2R#OL-PGQWC?$98LR8^WN9w0bOd`=+l3JOBscxAnvb#p_Pe)o0xKzCta$ zlV6v1W$+s)dfKrP#lAfvW|v&jY#De^W=&e3-e~d@I%k`*HI4F0lN45)x%AjuAIv}> zu~}KQws@Q3HiI{-J9ReXVqM@XMo{7_O0Q*|vqi1#FS)--Q$M13G6I<71&{danG;A` zn=CV7WsH(=40R+gPBRKBG~ine9oKC{!D%}j_ThR}A27_Egj{&dBdXwtuVMG(ARhDK zpSL6Uy__(f0|prD|(8CFUWY#oUjIGp^4C>4=+W$#J1o#Bt-)q-!j`$vW2 zF^^m-LVphO-G7RfU6@md$RCF*@=MDm7^N@?joNRBU2j8)6yV(ra-K(Hh5+WC;dmpq z4pg60x6UFK2i>~MV{Y{f9TGL8+gxXk2;o^qLsvo}N@n-`egGrP{yqL5jJ;E=Fg(;? zeQevdZQHhO+qP}nHlJhLwr!vDzu%usGLyNPG`(rBnw_R;yZ2fY)Qi5!qwwii%Soqv zN48o)+-GJeD`v8YAp`XmK^+zM)a8PBa%vu)+RH|7syoIuNgbGzn4P;{8Zp)NW}URFBRMmNiwmA zittrFDWutua-~+Wm&B;5kttQE6)<+4I%-;#q*_&Q^c3i<&~2ifPS%myTe{k1xdo=Q z(nZQrQ;h3LDb>$jfhcx`At&i&l2~F&3=B+O2zoqq{37-^T+0N=*4TQ5KlUx<+}K@* z6Y%@XW`{K(9%k(63oLiFeXk0U<$p1GRCMjQD$|gys-t#F8?)X$?0<06;$ZAX3EWQm!}pe zR#eoFO&neJO-&1Oc#1Bot#g&B3n;@(pG>3R`zjr?BClLJ-4Zj!5~!H=B~ef^)U3SE zJd%LGkf1+Px>mL7B}{?B64Np%WY(2M*aA{aZjtt)FcT0JtkOx``Dq1YSK6Cs#CR-g z3)nDxDPF*Kzo5YV(XzWMyGEHB6wt;nyLHQBB*YXM%?u7l@lX`$7*!hr4hiq3Q;)Y};pM_4XU{~3OxabG`EmtZ%tjH7o zt)q#xQm+yZKY6=8I%ajLTf3;z#0=h25MmSx`qUWq#!V-uNN~~=k4Q&ONrJa>ZDR%x z^20uZJ=+1J#?sWqAd4D$!P;STd5GEbGihoI^yJM3yQat=Uwj zhg;||!y3&D!4Q9d)2pi1FGXkqB ztEyo?%+>;R7KB)zpjq(jajZ+TVY8=Suy5I!T1O%SAlfeL&KciO-CJ_^V|cZ(7IM?I zhFYGKU{>tB;_D?MhfEi7OfMcW8GRSFmW#@|_#Rcq>HCTMK9A$S=+fOL({Ox*tCuoE zZ!ufwHWuqd38Bnj(@p!M$Wg-4Bl^R0?_ayaC>~Y`{;m0=B4=6^n*%!A@7cO$^)#9` zR?-jcD9|>v-@3zeZE?R!jq&a4{CYkj8Dw-QXi;;6Qii-XXmbA`uS7d6x-o3sBPbJS z+E6qr-&LNO{vi3EG?RZeR(<@(&g0^U)DzpA`|FF=*T)w37WnJxtMj|{zbza?Ol$rS z|BS|r{w~g$e=(q?ZsOK`_P-Xrjh-qEY>CFR--~NIEQkMX(kbNuG7elUL#rh2r%>Pg zR>&>i%GJq~P2llEWpj~TtVY0YV8X4JM9?1I+I$#HGvP4eK#DssBvOlgh~W1pXH#^K z2tGi7OFPGGpbT*`U=dI=ujP;X<+C&XjiBp&Zgmg*Y*k4vB)&<0&A_wyv>R@phWmRw zPVKXkyiQjDzQOw!vY6`q62IY-0L%GGhOP9W`g|mR3vr!WDUOy?Y{HgY(PS zU zjC|6&jeZQhtnVTe_V6hLLgv>c7bkb8dj%Nh7#BSf?hJi$R(*V)wP3%neoA-Uy@2J4 z9kE@`h!yWvtWn4|E*rO3nUEC=RwEXtN>$I`9kp!yL#CH>acR-3SLXQXQf*Rl9}EeZ z1R*5k<09JFO~d|$igKVdq7!+tNYVZm5%2`g|6#d!lXJg=^Kg1IHcsxtE>sF}{`3-t zCZ-Hsmt3O5n+oVn7S7-4~p{<=D|D|+EF<}+HPaTU=#ZYHA8V3r@lyhv72B{ zTg&x5&{CZ5X1iD}eZE=92mfk)dGXDv$!)&jJJ*XMgKpj3`*vTI?ARCbMFu`4IeH!H zY2`U&LP-5~09Rbn?GG%p*PPGbB^_>Hu%ci{QH?;xcq(l}cg(#Bg?plV7^QQAv`PMo zBy|jY_{r)?B()}TC0_t3R75YHe`9+_Ep>Hwdv$ZKj zmu0{8b}#M_Vg3DzU5B|x7v3JPpcN;Dl6qC5nVGmF5cF&!({*JQdmTYzmYup$va%m_ zihtkbHt!uDx3^sV6_7Rn?iGXgv0gvd^L(b$sMhal{IdDY^JwaL($eWl--G>a3;MBr zzhC5MOSOp_`@9KOe%#06^Q}E3=FgzqZtyVu)op4TYrE+5mNGF$P4*M8mJp}bsn+GZ zKwE%XiM#>4h_9lwIv8j>=N)N5Bz~*=Q}$Ou525NPhkW#Oix5(ir9$q*7MXkj)#@5lu7 z;P@VIx`DiMqBeZ~i&IyYpWB=L;IE)N@=4p%eMFt}!Lz&{NOlOmq)MwxbIC`tOOxEalp!q&t%?uKJJn~i(kQ^o z->%)VL=z6|VvK6MT5P+l=`zZWiEOJZEY@_@iE5R(CajUGGWT`Fk+W;)k@T+Qvbzn^ z5z-y^GL_$Sx73h7PZmL229XO_Em}s57BOX&6s-DKtCb)!sZuf~;8>-qqN42r5lVF^ zm1L(sKt6@8Vd!g~zYUJVmh+fT$0R zA01ra?i}xW`v(6`#^Z(FCu?Ihqqr2USpR%~r{v}ssv)$doJn|c0eR6SN$==9!gti{h zwIuKxhGML?z0k5hF)xC$OwqM5a(q6Fee8V76-rMJXbB>ZlR5^DNYyfNidoCcEs~KR z=`AKh9WtMXmeJFpKM=`Q%i)Rqh5^kkkFp9%VVdaY? z<5+eH?I7(~=}M5-t=cZ>E z4C-6oe}ew%5Pq8hvmrr+-QYIc`iDn(XUFrhCYhklYHb zS}d~O>^6OM?=YjPJJ8h;?Abq_&5#3YM^r43N6d^6Dbl#8c2`WH-i(=OAmqsbsHuf!Z&GU&p1h}RA3SI@j2G`NEmfMsY^+kSeygE;}JD%whs>f){ zER|<2jR^S)To>KZ#UaR#{LI>6uz*F!O;8MTDHy4uh0EdQ-X~^ZECX(js#RrrA=yC? zO5ZDbB^ao*hN&c1hjdC1UWkDp9bG42yRn@hfB3dr^XO5<+Xz9GV&5X2HDXQj>OHSw z5FG5z0_?tv03H?BOphZ=FFvp6BDceDsNq5UevbY&vUJZSE4TIVcut}!8U zR?2<@#U{S{akFxtpX~R3c$K-%mXY~$Zm-4fHDkmi?&jr)B(99ga1f6RwL7Szg2! z)tT|K=p*W;JR>>@L{qm8g&Vzl){QRs(TqiKYkD(?ZOyGrbo2EzF3zv2Z!>W-y6LW# zeVjEnPy-})vp{@8X(~3Lh_s@(8R9+0D}UfVF%csM&^WVkaXcnj1`_;u?Bwj2kn^bL ziVXdjarXKaap2MmQ&h@Jy>vea1=Q)AR7`;kI-|q~dZR>(Z{JybM0B?`5_U9*%g9tC)>Tv5989 zxjE2~&OyThzfgrn^;HobeKhJQb!Y_&7$YK(EUOR2KBIo?1hWi{5i@SJtnPI+4zCQ5 zkry{u_UJko7k&b-NvC|R7}kK)t9E8}XL3ky;BuaxwT4k4IBsVq~81aPFJW_v?T5p?A)Hl2<+g}fxEULMh? z9Bev?AT_Od1w91MMI6Vr6J+er*t@p@i!|>Nhb)ipBwZA+)w18FxA1*bBR=P#bBQ0) zl{ijebhex#UKUT^{EWWD=hohq=#F_b3yI|Yjs1MEx3*2idI}f$)DG>I zf!Tni8j{b9mW<~Nf$5NpGuG-B>{h7mw#YWFQv;aj1NF7x+sQa(3((lzLL#&*qd23W z|Gq?LhH;{x7_#F71@eg&eOq%eJvSDASW|~mdr`)`D7nn|D?#CP$f^D@oN(FImZDRA zDBn$$`Y0XZqj8~wZC$1wfW2)fnv`alG=HcDIA;)&*W7wZ(_ zJ+`tqxC6S@=;fN^vzAH+Z1l|J-NNOktI^TmW9#&+=48*7npU zb(7S|>LOFkJDQNBlCiC~yQ7r4yt&9_(c)WXW~roUWqMILB%Pz*rH&NKRyC_E+;V4i z6qz7}Hgh;COW3NH;()*hOs^9Mt`!XzuBVD}w@^`&PNSDTiH`dn7lUaFsGU}`G;Wjh zIt=zu9|Zhy{NePKnA7~a9b>XDt0xIdby~E_v^9vYf%r&*LymR zr`Gdy`3$RF!rN4^xjUG`S>Y(4n2;C$9vzuao-(X$puJ{l>AGVry%J zWy9bkK?5|3FwQm*HVy)A;%Ni*!jNop6hd0(kyvCYy|@9|Df4uETDOISf0AiFXp?Nq z9)sN=nIZdGOrUR(-CUfy{g1RMOuP4≧}THO>bd-n0iX-;CFoZX&% zBR48Qs9iX4&Jt;sk6@v*%tEVB4k~p{LRr&oskkJ#28zym=r!m%&w68|+p<(npDJ(y z?IE58l*1dXv7EWqwpnbiPBiCgO=s2JlxE>fFxwc>rO8>nV168Q$HnVb;Opcvb!kr%XnC)Xf3x-1TN(SD+H=&P?zaIt#wBbgh}jJ)>t}IZSnp*{qANU~8pW2scMQEI|&?f+HeUDjZhzW!cn7Mlbv++c5ci!>+LMzj|9 z7_g#-A7BM(Yjji>sh$=}+jBx~K-vh9R3jEt0vTwxYCS`o6}fhJ4kf0b^i94jTpB%< zg+9whr^_7mYlQpm`ykGDRW-f|n~wKEY7F4j?w|kZ-ytje<%~dIqV`xVI!5-1KX%`7 zM(cEk#8l290p(FBu@k8rA-&9?CR+kflCmDeHZ&>D6qZJvrUme=!_%<#u%nsEcf>~w zGvAP+sZQv^*jML>P<83l;?%2CJBD{C_Xgw;-vR4w{(8RSSqFX0U-%brK6!LeprJUc_)+!^uMU+yCy=aav^NAfaWy zuhGwDmnZrq`8`_>m+|~XaAjj&mBc_!x&O# zAGmB*$*iqds!3+q1t|tlwwWvIt%J;usSL=dgzIxf2A}!C)3RvxG3Of3LgaV)RbcM0 z`&2C0>r)`}fJ6;4nhu5N>gPeoBpgjY^dxzXitp=l=J^^#tL@o0uGig{kG6CLPytZx ziMkr6#d8Hw;?ggWLRv!wcb)bit+o;|g+p-+FL6L9&gfrd6UD(2(DpeoaiA1QySd$C zcdgvGsB+A_0ZFwVnQoJ807tMN$BWh2H%=sc$TD-Bd(V=g^EPT`v2O+C)sq^ zANR=YF*_{Hp|u}!XJdcbbG#1LRY-p{OpK@8U*&HK;qABDKD+m_J{9uC{Xn;!3fY0- zfQLBG&q|ON&8O>e(KG0AaiVSA_!u07ie9D6dCYm2@YD`+)yk#AAVUvz&=!nXwWnlF z)EE`-_m(dRgNB=<&Vs`69(WEo&pB@ld0}=k&KAkhJXePgXJx3+(=9UTWT=lXu92<} zo1UVgo1uy5u|&g?sjhQc1DYb6W*K^L0b|0?U3Q6hM`jGy>&nIWcb|`~By(zZo0vXm zv>VStQHgP(Z*!y;-^Plh@qL}4?xr;19vn#Thl6^~{XsrWiqBcw9jQNDb?EtrLp}Q> ztrE3$pn0iF+&Xt!2b%{P1T~0XCVcBy^C}3bU?dbdFR|`pls$pW8@4B(F}#MgXAs3$ zD~yI|x}Y_SaaCqD<=px>CVgdL#h@@ri-8JnzU3Q=1Uu2e`vJ746alIXM2i=`eQ@5b zz6#q{pM5vh*qceQ%)!^cL+ymy=k3qYH5welmtk@QHl00Fu%l`A{`RZ+%XQqpU$62@ z=JI)Zu6~EaZ?{}C+!RvdV@mmRy{_8*MNyU0*!Mi1a@F@daJzO>;hLQT=$S-3?ZJP$ zHM7WdsavIOs+|-HrBgajn~Fd^p`+^Pu@v2?eWJTd_r=~~L3_VzPhoFiL9;(-Pa9BN z?I}5Gd#=9MpB#U?N6vE2qUNZUqN-zR6RjT6nrIhcuVWXzF4D{P&`L>@cc3zXH3JT3~e?fpEw?v^g3_4_MLFOi!6 zYSni={$kho0sLxZ!ZVxy(LZ5)qwkg((WWsxvr#cI9h1S?v6Ax;Z$NsmKw#c^Nu&t#x>XTg2Q1#KytORdINSi-` z+mzgRCG|_h+tN#OT35VR`%-@OAKdS}T(6Xso&6eUKc=CLi{;CQSo~$lRmCy$CH9`C ziS=oX{oYr#`9H$n{9fTXvO3@gOVkwU9>ct+Bx;3fsrH~&mhsM>pzVwL73ebJFU*3if%^EY^6OZEjd{AuqPgX%l(Dz64>+jre|Ue@%Tvzs$&J)Ev4)6A*2wEm;$?G7E;=lU*NoOy4<-n8;NV?&*{ve42Nh|Gcc z1vdz88*Ccv8f;A4qp?M2%Vy7H(}&uJ`BFKWB=v=~s>$%esnwa4=HQJhXXr_h9$Z zd+fbBvDrtm-~J3wrFryUl=jjuS`!|LbNDCS%V9-w0V^VrDzfYfc@AG#5ouFK*S?ZP zRc$GtP!>f)J;m4<=2nvAPtw1E;pCCsL4K|djXqjcc=pWumt|GW>IZ163b6HC4qSk` zTm|7mpMxTaDxOGCwx{|f!PcFcBfZz+zVoJvkUM^Ig!gL{^V4lYS^B_wJb^H74|iUV zvbD3Jf>52IrVAEn8m5~}5Hjjiic$&)r8WjIO$Q4%BGa8|OhGAFW{ynHg)($hDIs)X zwE?6`ho&Vj2Qam7jKkSdYX!w;>PMDWw)MQtm;Dy^A*1|+zfEH z*=wJFy;^^DrP0;>s=ejX*S6!ckPmIKck8@>zV-E(>%O;g@BJopwK+%&>H67bjo#?h!E{Es09XJ zECrSVT;B_Y8Mj5C1nVRz7$Xm;GLdDgL`hLbUo$H&iTQ;#7$rLncMNrGjT2=U=P}^1 zEHP<4yPz{Tcka@MXh)*sc=pw@0BIz?cNp%wNou)t)ggfO>#EUb{t}B1TA^G$*YX zHSe#;_oSm4!K~^8(Ddj^=T%j^TSc>d3U^FGB?)v!W+j+JS?>J)+RD32mPsb_x;@Rr z>o9-xPNXi>g-U)dR1)uH?72C=vEvu~B+w|m`y!t@?}tk4xcLy4oR_-wN7QZ@y+I@B zu|B*DwVp@GW3sB@Og;ebWsON)&M6@MB!#TVt?pvtpA}j41Fwcpq3Wc7F zLyx3Z$m)pK7JGkahX`6Fz&#lnVq=w$UO@U~Gtk`@xh=dg!(-v^<0Ih-dxzRb@(ajE z(ia|$3Lo+psgK0Ze;wY9iUni)fk*O3VW0Er?d(c}|w zfeMPiz4zHGYGhX5fUCl^gl0aP3~0c{>E04PWZyh3To z{ueU(+NaOG%D#$z&-qs;|MyUW?W5_-{H85<*vwjgHcJ(DvaXejV2U##?)!&RKeGJP zoqUP0&FFHs&|?nc2Vi!W@|zeT?1%s{lMf}qMy@I=mwJ(X)4rA8$oyJ^KQxI`R>?3i zZ3oOxLuq%|$!uT%6+&<0A5j`JBpEfm^!NZrY^YCQB}5Q=eq&aBBU@qUDB3U9l~Y+P zGzRy(+Y*y@_K(!cdx8aH~e znP*Oa{@&CzBfm7SIKR2qs1FM6k({PKl;%VD;Mw(AoBkAfXMKoJCL#2@STI3U&x8#c zX*ce5^-l!g#AM`!YM|hPnW~==`(xAs-4rVraml1uwFTQ3javDL(a8;)cI-HJA*bHT zT4{vEBt>)2j9r_SNcT8X(}>MzcW0x9DfTWrIahxQ4a%-k_t2n*oWdNKNU3zm;o%Mu zZC=3|SO^_AYWtq;m^R5Om`qaOrpTip7|7qX;-&Ohda#Msy_?SU)l<8`|0aBz%N~=Z;xyDx@kMh)Q2c)s3lgga%E!oy=HpuCM=FW0 zfigRZiH{5B@EBMW%8aEQzlOvb=ldmdcudq}xUuD$FY0pTo*C?TyhhdA`2;O=J2Ja(y4aTkwKsf9Kg>F5K+RuTkyi{nGgJ8}Yw) zw%=H&d+|S@O?t(nFX)Q&*g#{wKd&wQ=KpwX&3p*n=d`Ae6 zG#`sXRsTV1y@rR^ru2=yV|&ZAYyPV}I@DWo{0#XhFI%g%m%?YfgTB-7qx(@xF^w~B zG4+!pfc|024q5xKWs6SHJx&*y726WIV13RKknX!?`m;uxJe<6vs& z?^J04nh1NhlrR!>ehR`+L`;GdAB6^_R*DBGMLZ6${TCT6Y zSVD#9=ap7_v2D{eA^bzVfgpb@^E{=TD_K!KCXc=?BkJ#5zA@hqu7{s{kUoC!&C93C zJwgWx&9t@Ah(d7O2WRpy-HGK@W%k1xQc+^N+s(vNMo$}xbJzu8H_L3;yY1n< z9ycL`Hp?JKI_*kE8*uThmIZq%^QPqXw0 zdHHF~R)tbaf$%`PQI-J&II?YU%fwtgvt%Ma_9A|1D?qcurCDGDewKKbT6S-3NTAvy zyG5+ab2dW1kvI1yaRwQ}!_hGjkum3M7j9Z}w`##}0UG@rAler(y_8S9^n9ig$IDgj z?^v@zpT_o8%K(W#^R)Gn8`=BrtH&UjP+qL0jb=gi~##&0snlD>MEIozYR&Ol@&r7eGif+@0+Ni4hCFX92U)-+qc>%rl=4Q?c z4a}>j1?jo~{Jbu_Vm6dFcunfi_{Kl=*J>nfxu+|g?>*A~qiBreZT_wDAY3h3U$+wc z5;^2E_kHJm4fVX375BwXTqk7wdPE-?R52t&@+;LhFrTQcwXHV(8 z+NEurudb=|5@4jw0es%d3cQ5xV6*=N%(K&B0tS6{<}iqND3pg<_DG$nlL;ciu_$0q zdepi(HGyECNCM6zJz7p@07P)pMf#B~m@ysl`QOs>CHPV}dYx*0s!V+A-qT9cyKmoV zG5&Xn(Z=8y$=dzfk~h8%EDlEup2<{3eFEsZAf5(+#LNR3yqENKZ==iS`h$ztHScNl zus37?#iz&PPROh7E;~c+@tgfaf6K3SM@Bi3YcAZHbkR`5}^Cd>Y*u6LI zX+kRXsv)sgX!VLAo+?KBRYL$?)AgER;t%5m<`CJkeeIGVGgiy(sv$J{gzmf{^`^lO z(--oM`_}3?Lr6-S|AHYF=1D2nbb*&=Al!dhkTByk^^1mZm*B4J5uYc9xTlFjgcl6~ zY8{iV8YRJP?k?*gr`>~GHeoHt-CQq5bL0)v>S_0|6qS4kM zFL|vW1_y~iv<}`;4B^%ibRTjFFQ|)-AcdmV7^IktAVUn_6a$!{T$D;)na@ET`3qhW z$WX#XHqqNL2sEEjD6O!hZeQ(7edoXdJ&yAvZwu&cy5f`n67rvopz{NKN%9YLdq1*;KVI6Xy zrZpE5xG};G_A+PfAqL|qK_Y5dZj`~H1Udf<30k4c7zFO67Dh2GtCV9acaCaJsO`Nn zX)-CXU{~d2ZKd{O2{#-?C)VQLO6qUfN2pIBFZ>^nA&P_Nf2LiPMYRff2iFuIq@^&S z!6NObMZjAS_Q@i5N+MXonw3Y5)~|84m2B3slDbo7--&MQQY1(s?a8m36^y$Go!rHi zT6e%9+Qh?BWKAk2pQATp6F&q8B0_h-TM45hD35{Nt z-GoJd8eOy&xx4zKYexE=7;#WlKr$L&kVf!XxHB^YD}Y|S&aMIHoa5@+QzjohT2tuzjTBsx_HvMYK-B zUNGf0hHm~Sp7C_t7Ywo(W2nq6BtQCK{#=&7l@s_`u*om1fnV`pm)e|t=TmKmyDl{Q zZapO*?6Q};>@$uqP;Bl)e`-^|gY`~i+%bPMH|MRLTh2d_5o;f9)>Bxa)t=du3wp0i zxn&1)?d`|vXIhQpEIT#Gd_Zgd!k&MDzH;J!__tZ{Kxgb+_V$m#;5MvSIfHtRXXk_oN zc^Xw0-H3yqb%Cz^2XNrRVcsTnETYr8u^EZen;VJv%Ts=TUIgMVR)3g&fKC3mCsdMo zCClXJQD%m7Y2{~@;(&96Kf72QORe5r+;z74Uaz?lQHRfr_v?iF>5MXSK1L<GdDVxOgO$kZ5Hcl}SQp;0|*-qmSd^8@Nj!mzZ&#V>0&ex9OSroh4i4MdRoZ zlQY=94TsfkMr4#Q$hi&IoCK{WXf61!aADTL(+xg-3^hGoVx{;5WjX;9#v@G=s`LpV z4;#IhY#HXM5NPbffffESmh7E{Aji2*^u}uj?6Epb_P%^zrUH?#V}+;%!3p_$U_D$? zh6(6rWoVD%qngjVv-DDO^w1{p(3uEv?C8hw)nW3`tSZwc$Lu$8k#IllSO3nczV zIa?C>SS~}N06UKP32c=rNArN=fS>KwONk%KcV&oClm{eDl{eFy4T*N)FtW>L=5G^k zR1&6OMfFeGYht^)UkJas!<$TOdB8wIHBa3uOtq94FS@)yLK96sW*B)ug#%AnjHr;y z)RO`A8#?upk*Sm$4|9Z6(o_{@bLQ;}`*griFt>>XNpUqKJWN^AG&V~G+4%BblLt+y z7-Q0KND_@2L~(6 zj4DMe{QfFN97rL&1%AkY$)wVe6%DFdp&LZN!mvoLkOmUIGO{26eX2rmCB^n2tWdFl z5t4ga4E!L&1Ok{iF(SZ(M&XwQ4j1GYm{>BUkQEavqM|crU}*AiZ$+d9hnN+~p*Dam z!YDpgXq=IW3Gl(b0?r;jJ{)l9kd+At2adRZLkb;4F4k+Tsre z;=&n!lLr5}mSO}{s8Hiz4HuEA#(7g~MvSC@rXuhLOj$TkdU$bwm#wH&su^Lk;6sv$ z6Zb)Gf!gCLk0uiLAz)yuVE!Q}ACQQ}RL7GXE^rK}r7x_Y#kR0s&RdXVSZIRZG_2B9 zX!i(IUP6Q;Inoq&mZ*utbN+{gArW|8Fff8Hd!Mu9n`Z)%stD>s%nhot?oM49NKq<$ zcA?M>4t6y$up+69gBNb?nyt%IWv|g{YcW?sV;yTRBLoWvZ4U( zVua|)$3#c-_K_e}jnFdL3gi$x`Vv zLnnu@j}-4PQb=@QiGd7~ljwc$Fp^eMwkaAaVc>(~V&24uBoBAw50Ex?M@hjRg)hR& zRA32R<8<*955cbnGmjX;v%jjyBH0P3hpCLrkP{TbXRpk`iHjEl6Hp%;yzDqVG)s)X z3M5gz#{y=yW0WR_iRL&`uo4^1X!wtHI#69qDm=?J?;9Ei@d;U`YlHl4q>3^H~)YF6sGAoM5HFNz~qDyDULH8wVORfIVZez>5M zt&n;WxzBM5nn3^q#ZVjh798ppG#3>dTt6a~1ra_dSjsJ7Lk!8hdI;(dxVfI@D`04HEXG9QYP(Dcuw53{bH zpitn{j~x=tQ6eNb4e+#kn(&nL)bu^aM2?Yvd;pasQ1J9qIY_&=hW>@g1_WPwde(ev zgY8v|i-So9w-gT#i~u3%j>Q4QJBDKA2Fr6px0!*KSt5`H=I;4oTQrUd~Q2 z5q`_3g&-pIoJA8O`RlC!v`^p zXDTVcYzD2$Aaz1;dIH>J3<9_SgccAh!$=8CKr45dYYC?}$=Ml;8!G+#JY?+U^viLW zD=G-+hV8-2Gzl^QBz}lJm8U@zF(vu0hge6j-A8RXgdY+7rk8NQy~;G4Y0>E&*!4Yl zGTq*&s9Wojd-`;QIzM3vzd>Gg`>D;$;rHq_BLCm9`5&j}|3hs4Gwd+1a{TA#{9mF? z1|~+f|07ucW7tVqd1T-_<2mzeGx28AUUXxhu)`uGgbWcN!G=H-Ay7ePLExL`>RopWTq9P`_*Mxx35X3lH%$lgo5I zozBc;HkSqf0#H-{M>>wl8s%jX|RgaOQ?z#zz$0zmQOs0UerWLOxA zxDF?~xTP><76@7>lM0afc*PLp1I})+WfG#N1T@@CZiw-kAAF)2aaR~xsCLbdbATAa z6Y>ke7jlJI7yS<*1nJll*^oPqF)5z-evLu$g-$2b8F0`mL=oa`a>x+3ZKoV%&_XZX zc_+}Agt!;+3SXsdNLRxC->cNFy1^_zPTjyOoWve6Jbb81wP2`gfD85Nmcws%=X8WG z#~%$FwF}Y(3{E$F8@(_O%)@*{51gR?jOVb{2bQ`6)=M+Q)twMVOas(`*E0OsoFE^1 znmRFVsFNka6#@(l0pu6TLN!4E2lUw;swR7c>k-%HTF4`G<}Xf%oHQL+N@V>N$Y3`F zIDOKMlZG$kbdVCI{58IQGhQLLFS=KeUXYLOBYWWC0cM|0<_)=QGx(4MfntjkVaTB~1_TAO6La6h4j}<~+6ZJl= za@>zM5SdWM1IaEz0Q=?i6LZQo^;2qVW2e1cx7E;aF|mC+0q>K$T}C(fHaWu(F-34H zx@9%Aedno|K)85WD{W549`$hZ~2|?FyQHx8&w+Q%w99mmU+jhXF z+j8`_?}y{u;e@_<5zi3d3k6)_ZVf#;*LG~2^PW?0^>@yQIIE-r84xdWPc-!A@`#&`#KzG z6=sA6?7M=TeidH14$aY>Htvt>15xeBk5+X=pveWun0Qu{fAk43Fh0KHPt?sNaz@uq z+O}gxkA7_1%wt=WhS;rW??!k%{@xO#Pvjly#xd(&p9kB+2ki`a)FNcX&>J?_rhSgx z5c!aM>-vd_d!}QWPc?n(fp;x3~#@RQzz`r-*)*esUTGP$wnS7G2Ssy^5au6N`O&zDb47aYC{!1kTG zBR_YDmHh}yAN?KBP(G)f(Iu{4eg*fsDdg(N7l9-Go1laEQ-j`RfySjpZk^#WD8AB% zXv^o9%0B9WQ|?r7@5B)mLHBe71tqrF~-du zVlGIy!=C?8i%eL(x4c#ps)>Ywtw_`WVga6t?$(G=n&B8D~yJmIX+ zH}D`~h%5v+!tVsv&<)3HbOiQCZve0_o-w+SjX%x-XUN>vCLI{xTK3naN|uspg8@YUED%{)BDG_|9_7(C=UMU#4-q<_2F@YVSVC1)dIqB z|6-3ZHuuOn(u7NNXrDh2K7CE{8~i#e`fdK^)w(2t2mC#G7u%gIsP+5m|5P!@c03td zHUzONoEXss4f2isL#C!Y@Mum79I&qlCZRc7Qn${2s4<{ED$1FL32d&Lnv8-taV%)l z*a6y~DseWagc<9xu-fF|Dz>oTDunj~T#-2nZUcZ{bq5cKdet0y4|8(RH($$VX6g6v z>CnAFzi`KLJSm3t;E4Gy_XPAmsu}5Ln6l_;TiWJ4C+29b{Y4=Rsd?J0B8Fl=9!R=5O%Ay^9H*NCB6{;pa%k7 zStA};xZ>4TgH|!VAr5pyywJeeMIWWe)4anywcABTpTa&k_c*8BX5As5^7X^+0T%tm z1*p0Im=3)W|H6KN$JHY}5Lb7iIPj-VAWSv<@TYhYC>D1R zY5oZE)m)@<4wbqngHtCaRZPR0Bs8mR6!4Vu6x^upD8f_tk?iHahiqNOI*@lH?ee>e zxyN$PZcpi)-W<}q%Dd6K@ty5m`7aWA7x1#lCyt(6o?%|L&o$3B(nOL|NQ{$6Dak0= zCQ?)KQZkpxEmamfOtNgGUOfh+d%5Z-tY5f#9(Gae!R`Cn$GJze&vXxUZ+nNlzY*{H~}UKYV$8SNa(H9{MKsruMk|?%bmCE?x5S-1G08`p$jg zpGeQ7=iqttJ^cglRs$pn`~?w8#~$9wr$(C zH@0otHh!^f+qQG^f8TR=?#{(L{q)q-)YR0}e5Y!<`$M%LN5`Zp>k{ue%D%8+S=!c* z)M#k~X>lcUDld}i2W!Au-(v8MyGTjrJ*%gC%xk}q{-z%Y;Ou+E~g;Q3}5%ahs|LEh`GmDOs_v6F=o@#pA zy}RgeXbG>hjib5^p+~j~TK!b>ft`ZWDX}{MPhTjaGh>1%VRRzdgL>I61J(qL(f<^{ zMEnf~tw$^67iLHEVD?&hXf_T=ZqbZx@|wBByI!rYGZq|Apn>1N=BA0zOm5s*Q<#qj zzi~$GRTb^8Im7S3)!$56#@BSXh@or2%?K$nMO-4R6mP#^Ev`?H?hX?duxuap4Hg$@fj&%SP~8n$WWe&S^b2Pv>~(L)4Q^z} z^DZ|Vgls>^3)u!JJ^=iNVC@X@1yQA!xYTd-FKU%5)~kLtPxRL4%e?L1Y#(8MCQN| z(z`$Ip;kj`4}WJ7Xu=3N?jYPzrh2ar=G|9r6P_VNC1f!(;=w=;M>7<``3g;w1$QJyx2yu52WwV+%q}@=lY zmj#I^Qk{w!mPQ#R;8>KvofXzGMGwt`q>KGfc&>Bt(uFwUw9=*S%%C0>@TH5)nX*h~ zq}ns9W2NiUzNj||H%T{XX%bY#DvFrrxaT;`3m*PtXU0g%ig6Z}6lTrQn#VM%ddPUl zdnkJ-dZ2qhdY{HVggvA^6g@;a32;z+Qh!E#MSKbP7L}aZKEQltWf%F*I6iP$|A1E$ z3;XATcJlEIV;X3zsMd$F_10J6o`^l6WRtR}dtK_lxYAr3xGylDsIwV8S9!Qf;A@Xp z?k_->CUlJG>i&n_^MvJ@)dkoxvaM@b<-E{-a(%+%9N99(uZ3R~y+C+R{roLkCc9E{ z0l_)OW0=!Gw?h9zTnqP}jBc=9X}REh;^qYS=<(L)tcX9!J~46*ehvEQ_tei{u--AV zPfgWsuc|+Ra}H1S)0*W{iSu0eneG^O;xhNyxPjUPp}(H1%<-AmD$&Bm2aER?ofkrx zhccn~5N*$zd~rZq1ty%N#q@6$Vl2fXF{Eq+9S?FF;W5Sa#3dZ$Sb=4X+UPeFN|*;a zbvXqn4JOyJTj6zNO&UwCdNoJ>RLZYNG>2~jYz&_37cXR*!DJ6Q2GTx$tp~DC3Z|}F z>0$po!D)*PA-9D-N@vv0aXv{e9dl58>)3-Jj1IXeLAX|lsg=h6c%q+c&Zc4^V38m}$6)8U$5_FEVA?++VXgP@HHkmby;L@dm%*v%4fbQ?xfr zu9-ipFM0L!UULD0!Eo3<+QNsS=eDKo_C}B77(Wc2)>~xKC8=~SJOGiP?rC;sif>^lX&uJRQ#7r#nA| ze|~6rCMI=7L@{=@dzNtl-=dLrVU0r!pp4bgPS-kx%S3VV<*z|)#QQ<)p zDO_IRl4o$*8(*I(;Mk}pHmf?+*soQ>)k(@LzUyT;}I470CIm z9L#M2nL1FU{Q2YV8H0sQj;}P^!8EmBHgSgVr)!8f29D2R`X9Zblb?=3`oB4 z#bv4e{oYv8_=evRKPq~Q$4f&!(lDRS8BK+bda&ZQpTq_0Kvcu*Iu#Z0flKDxq-vik zs?K)n!B%InM?7b)_*EL0{b8ZSM*2z;5C+%wJOcLlHZ})XUDqa6hzhYM@Sy!@Qh~(2 ztGfQ-&TEuR&$_$0q)Lc3siRt52h0#e-n_0lE*Pry2}WUJ7q_v|0wD|(y6 zLPE^tg(Th~=b$9Rb_3RrGU5?bj0jEz#ZfZf_C^z> zLXq2apWiYLoaU^|R<;Gd)6)DRVQdJ?by`cv4dLalG8gr2kT2u4EFFjh9{JhNT$GGk ztdx3&`&r$T6Yxb03FKHy3AJ4MPg=olOifk5%$RM>Z7jnuhcwUDG}cxt5@zFFBP4aD z_%1EKO_^5nJ!LP@{xpBjaM5mT*@`*z@DovPbg8O{I<4EDkQCW@ERKK3s z=|HzB(NbM`slC~pC!ObAu?KAIs7CA+#Rf?^$;d z!+O7edr6N8x;q2g;jUk(U^Grs#~{fDm@q1#n4h8brjG>{2{DF0^Pd|N>5KE=R061_#MT1-em!b_^2k10og4@?fnL z&z=KO@z1$C5oweZTlWA8(r0!lJSDwB9p{ztD+V4ia{_F?8Y8m;k(`cZp^y**Zs0+k zdArcby`NqO<{dsZU0Vt~s}&Y?iGLUa#tH_4krnPePBAAgtoWeE=d-y}`=#=>Bi6LD zV%)XN|I~dMfpe^_3u-KB6JH9MM)-3}7Y4;>j_~tSR0x}pTFgmoCM7n~lgI}p5Nja%OMwkmU8+x)kR_bPMMh13q#w) ziQ*8nxpkbQ#t7w;e_4%k5xsi&R0vj+2oVv*F)g*8IgxXIy3zSl5Ooc3ROn13XNvp7 zgg|bupY~7Q0SFLr%wLWBM!-GroYEa)9K9XA<4W{$t&d|-432G&=8OFKZ{LvQ_5mBm ziue(a3{Jw-kvvXHmjC&V;Lgi*cV^G))2a+ zuFc3qGy<({VC}FlL z^p5HYkj?yq`WuXs@WzVX4j~AZ63(TS9GgcwP+!rV)iTf<(P}?Bi)jx7fFr=vBM*Wvf_)@@*sG6iIQ>{OrI8}96O@sh0&pD`a z)U*aa5Rdd_RSAf)lx>I~1j?{lU@b--+%B5li`G9o#=dO?wD9y*2uFk?{g+bVXpMva z1Rx)Q(=_s*_!p>5@PPX{DiMh?JN!t~PN_(=#le390325R7v!UH4oChI{{kHLvAF*P zRFvHzkw~;{=Y}W*pZ#NG7ahmv;D5b;f6RX#IvVF-g#SN5#9<$a3;0in*c@Vsz}X!@ zLa^C4Ms`KuT6XM+{@0`7*c|+LO}o_C{y-ekgTIh)$PWMO0s2Vqj6YY1BN%NsVh_U} z4f%f~!7x4Gzx*e1*rqtu9w-~Jhf;&b9p>u&Z~Rk_gYsYg6C!-Ddt>k-z9Vd>wi8qjS`Xk0OzI_@lds+^y|A01?`}#-&#F0%v?77OF-2?Be2t3 zK#v)_)_W{GmAPT5pP1!}$!tq)q%)ik6EOP5CAk=9%?tA8hrj5_NnfJ{9Cs6)H~X!rQV^Bbtc8TN+<6SK<6&|F z#y!0>A5R@ASY_R=r#coprS%4M6-7f^0q)!tG*tsO!@{hMCcj+*l%i`h+R0koStH4~ z-B~vZ?0E6(4SC^3S7a-|b54%u&xCnJ+z)_Z;-G5uu~NtLW`+MA{rJuBX&T@gqSz)pq!n z8k+c)SWlxLJs+6Mj0sFG=?y2wU;YBl4%hY&i+;+EWE{ZG1)Epe5l2Eg<~PbQc#LuT z^83J!@xw`AXe24^uFf8>vgx`I!d0r<%V;D7%9`}gcOmC`mHLY)9e1Xq80A~vz4_Ni zS1Z}X+kDQd?-QfxRHpg|J((jKUk~3GUpSHwMtPy_1e$b1U7vdz842 zk8j#$>Lz>St!x=UG$3-(N zUmI>kfC)(tp38hAq9Ig?84pgnc19x zXo0%+6XltXR^?r;bc5*xG>`+1-C)pqe_imx3;`fdu}fh#;Vsm_kIN6BTb6uiFIJ_Z=3F1Z^Z82P8(X+ok!@i~#E?8W$o5s3#>ULgd z{>|9BH|@WZ1PZUmAr7``0MVdvI$BRQl*je(AYaw8zu7F=_Q%-HL~I+_-i;WIyCRWIc2_A-PUYA+6#x|FbsI zT(x=X_NwAS^~I0Vjm{J=1^xkR{Egs)s6l0 zp2z5pibkc>R~dsF0OpX0XGk|+v4c=XD$}f-PTC{PUHiLWR>R}p6&bfaD@}g?DJkR# ztJ{Hhz>^R1h9qf%dTl@^@#ctt0v}qx=mIG$fn+ZE1B9`V;9I=nKcrq7E9#AxoNYz2 zAUhNp5PSoVC)P+==~_J32k{qwE}-L3oHxjVjKEnl6p=K}nJrszyvOpHX|w`f4tjqn z`K^G>j|RnZ;39C%UnM*6Yy{VW5ocj>4;^~2o}u2jln};wA$chPmWPfoJ7$jwRx^@m z{j?MtKG|gh>QUhu+7QIQXlMot9aJa~)=-;h>G#a zo_E1tvP$i3GiQMA)UgfFcHe0|`&_fdwz zuy@?qq{!>`!-sZ0EO1Kj&)kWhw){EWu+0b3>oA%acSN;X{G0~YUBGYl#m?z1m?Ft6pag0Qi_ zg6@_B1wwZI$*F1(afg^nL7bo8K7$L!{ObN&&f z104-V5c=l$^+z@LnHvQx>6*6}^Vg)tuglZFLRByUS9RY!7)!x`7r1|r$IYEU2vG

2^svq`#;l1 zA57ZAecMXI3R$BQ!#0F$y;Q2=AWxWTbCiC83RHY}V5Ua2ssE!tTDT(86z7(SVN^wZ zJZ9KA7!)q4kU?`9^P`Lzt}Nwn6{i?U@B&<4`TAnzu@9@?t!?EcvZmbGQpy7BNglGk znnvxi_y!v=2px};CXn=G6lBcP!AD@?cL)}lStAZ{e3Si8wrd@Ysu!Z zvG{DJoL3+pn4BOU?VftoZbxxC&AgVBQBc~VrEqVf9x5+I5f)IM%MmoX!3=qBK22C2 z0u7KSzFOB6Gnhnxs0YRW_f{4_7d!g z6JeMUi~Pb6?XC+ed`+NM*BE_i&}aLAVIwA~W4|TlA#KtTj-*VX6eY=9+?>T|F%wgB zWo*4GZ+()<0a4O4S&ZCI!*TSlh*nZoMpV}ywfURTyWhNiQ6Ez=V^x26=%PP3u*mpw zB?~A392x93!b;VT8vCNX9uXPO2M68q7#Kdcx&;emt z-(pEyn4Za?%G-CD<=?&Lf6O}L?L8g1l@43k2dpO=<`Avo&T@WY3L?MptAY)c|%S9KSaaa8DeeXon~ zF1-i7?O;m209bvs)n#ZEt2MB^Sv7pg!2tH-*CEg=}yyz zq&H&wX@FG=JKTf~zQf;gXgRbOx=9`NT^3=!Ucq7oCt)Fj9285d2~={mXM@tx(vnuh zlZB-m8ybokeJAQ>lozn#`MB`tLT?-laPfAXp?l`P485L1f?wFf}p=m?>5P@ ztS)7Sn{D>$xqX@+X{%RfPdumduG@BNdnzprdIomO+s@kB*B2$7TGmqr%uza!OzSlF z<|fRe8&?{IoO4RgSLG%{OT{ltntSOvpD_Cwirm*4acAh?rVwz~#$*;W5Jj?8Dp*Bs8Q1_jM{*Ir*uu*qVfWj&$%9YH{ z5cc{zWWiW>bBhdVe|)JGBUUR|G5$;q&`d&D ztoIE5%Ag`3%j3vn$)n|$`HM-%(*7dZ&*zB}nz|7r_76!vwPAO+cb~65AwT-H%}-tr zmt6oR0Nonjd;6cbZTHK_R9lzL#eAF2mg&7$v!l9V6npU_{zZ|V^YARmA@ZX}W-9}e zvbfNBq#sk{9CQjP_5B9?vnJ~*%W6`F6!hi&hGdf@?6~xJ71iE?g#QpWwyAWjN9NY( z6KHNWY@q_PymNwu&&9{U>s3-Y>Je(4-Ix{@NIfp`wtEYyVGpqw)9=lZG51&{@XeWca>mmn z-Y+NqB!{xlXk}bw!uX0fi&%?ji+Bg`O|d2MGisGRau_VMIk9ty4^MJ<&yHW3Y@%t$ zwB(}--kv6;6))|K?j;OZ)uH>dd*Mv;MTnH%tzA$mS(MMYH2=d4sA-Wrh>YF^m-3@) z>Lqne*I;c**}l10`&lvqT4FioQWE2Yv*!KZp`c?fSMLu~WZ$4gm0;UExBo?7YbGeO zq9<^U3NiRqLQX;0c==p1`PLlU!e!fwCQu0bU2;^kutBu%9{N<@OQX&z^-OjKETTE+*zed$8=%vQTj_ z$$z~c7E_u7Y$2G`p*IK0BO&{YH(atx6B%0iQGDeGM?m~wk8S!vDMRZzKS|JsQwcS^7SBhs(6dXN7cy zhDd_$>CbxbVPfxMC~_QQHKPQ9Y|5-6PhGwh_fEKw~QsCRJ2`euN;|I}c<7nOW zl?4Gr3n0(TBgAQ`&B*WoU#9X$CJKUv8Nv3ZFUsqmaVa>ud*3QH1dg6*HTa zNVtpV*kfpmG!wbUEW@vYK4hhs2uNBI+5&h6b5`kj2XhjG;Y|g;9ukvL(iM_>r&DkO z=)cQBfFX+m`TFo7sR{CakN)Oe3Z?_^=lXJUtQ*ER?F%7d*$!Y0H ziD`y!o@0}8G1ZwK-|Pr|zmG{)@luwBNsD`YyduukGjpJUxGRH$j+f$&RD+#%Vi*rV zX&%5-{!kOYoFj>OPd92>IT)tkD?m|7)LdqnhUe=#cYbe;p-zKkT(y3#y#SZA zS61r3hAw{YbzQyicXfdC)*f&uM6|>$;?PEL>rIIFw#~{5U)_ zraPddnY8wcE)=nCN3UT4?*I-^U#3V*wFbM}gS%V$fpqt`O=JLm*yVxb_DqgL2uzpd zyJJRmPWI=ACcGE1=1yw zIU9xUYE7x`d#o}gS81s-#{v<9o<=y3Fa^^Lxakq6hQRz+v<@sqk%pJ`hT*fZsx=;L6KQj$=DB8{MQv?R%@k5iE6veY}u#cB# zDxVsz`YMqe)lJMrY9+KDS(H>pb|Z5HDGIMi*LFfQNc4mIK3?)x{Oi`aqCdxk#^nV$ z>ZSM3{?2Tu@4t@vR^^QbYI2Y#gi~afdLe(rVOVQz`q)reA84>%Fi8URcEFT;u6)5I zK{!da!R_w-*gjXJQrIc1&qBPPti{g!{^hm~HIKGVSeT^=De0Ws)NxoLRyhMCtG#P^ z4P%RaEEkK-uh|W7rSPn5qG(z&7ck$>u3$A@E^%s!3f*>RrD?>nIq7~|a!NuYyQfe1 z>$Y>o#-NFvbHFiL^_gHXwc19-GBqA6&WA0lzKCT2=@0o%9LzCe<48i~ICk*@6g;Xf zt!&YN5j#Ihk3s034qctu!Vm=$6SXhm&o)Grz=Q}kc>Qnxti4je_wM`PsBCre#^5 zQLj4=&Iu{kCqMa+sD3q~FCl=9BPK-K2`(uVir$@m)D2SH#fox+?YLOITZg;)>;nJ} zxMrc(eVro_RM4SEg*$szA8%05-F*k}S88O&tr9Qma?|On12>&KG-V~9opaE$0=T5d z2;7-u?-FzxQj$L#L5Z+Vf#feZLkCmIZXehxaDTW4KF*W`~{T z7ATRZVR$ffO@n*J?6~d)ET(}R4+!J-3JAhij4JDv+{g-JrYlE>RzQ?pR!wX}QrkRk?DGoA>&%`~_dC7bcq9mF}DH{8@W~q?{3aU&b8;yzvMwCk zF^gFK4xM{j!0O>7@hu4sWD*J(6B=BPSGZf|IKypRvLEC3bwL1E^KQmKOs}=|hG9n) zG{}6~tM#}A!*PcMDPxR_5546|;SA$wir^BUVbF+tF=1L4!U{M_r;BC^N-!F}I~K9V zN=5jX;yx*_H+Scsn9%H5-6(N+O<(FTE2$#lNU2s^dqbwhx_riSY9LDb^A^X9ALLILbXTjk=gqvP82ys}o2@K^>9mOBkcGc|cN9ZgZ6l|i zN{FA%JR51Jtt?P$&2^&~Mi~t@F%fahL%iCp$w4CX46fB1f3-}#{wQe`L?^}})QPw$ zaeO^+NLv~OYE$PS|HknFGN{vvL1lm<(x)VNg=iE9mU*&`7do1Po~F{V{Zk9UxB71@ zQ= z1BI3Gh^Za+puC5P$nuGN=p*uK^xY1x12}OLlRG;Jh5~MSuC}HKlw}*_qqH@fY;eaAYzz*om6sfJLL&S# zwFT3~w?9S3@R28S%|?cn&{wP48v*vyR<*0wa>DBcqNQZz>c~W`n#BsvWw(LiDuEs8 z%3xjj5#fC(5lA_(fgO9W2;i?&u8ShCPGHr#opmjqRneJ!FJ5KKP|}|n`{7h-o-Rji z1JXh9Gw$lfHyw%mLEfgO`M2X0JeBRfxe=s1?A4a9@xi$fuPm2W%=V*`8>--jTh6@I zN-^qh-d{Z13D*lfqThJ^IwjlV3ww!KSQ_S7c-$K*r>!wIaxA6S^UUUOvqX3DlLVWt zcdld49`h=X>kqhx#*da9?$I7W^+GghL8v0DZQ+$t#xzQUD#MFIvupw2wBi+}nY(nc zK?#%1q19#dQNvmdLjVZ)<3dV~ZU1q93@T!>qL)te!7BijChKR!0r>XE>DA6PRhZ{z zqxt!gbVhSF=_Y?-5@6Z)OZDcEB+nY&dR?W*e4V9xOBq0M!J}0Jd4Kl)iQ6Ia99Ep{ zRVJs4k%g~H`MCHTMf=keC&Q?Pxs`33Bmaq5BJv%E8rBqkyzHZ?lsIZu>$yZG;5rqC~T>D>W6kcf(=&z^fjJZO*aPym14xjAr|a$)bblYwr{F`umDlmr;k{ z1}bGt@dCO+M>w&wgzS=pX{El!V3KdcU_EPJbi3VYBJi-a>CiAB2V672)ITQ?){>jx z(t6`oZ+}VDE!vyA+MB6*69)1ox2IP@=>G9_DgVXNtgL&%L%OqlNf>E?{^>)aO~EtE z$IYpzi^0XM5A8Dfr6Ha1ZM%wt5o50vNlghxRlq56%uE5tEl#Yla6f9ylm&axy=O>B zs;>G5$oDA!F{j(=HI0!Fsx;2BalgRwf_dr|vh;S}N}fULv=?D41+ZN(wx{y!Qq^oh06Yzxg4icl=xSe!>Cs zpVA)}-wfVMo-{KUqfjoLY$D0kWT<{2!Kxs0=8q)Nb`kWsS%XbJ+(qVD_b;a;C~mF% za28l5(w`BrD*a4#5i}VzPKCyG#wCZjLF!UFMDex1B+kXS^9Iamw4DD_M=4>bjy!E7x{b6K;e=GyLKfP+gPvlCmI%uWE({P;~f~PDO#^Q)=>0 z!vyr|vhBo4B-S(A84nV3qT=CX;w7fkXoQ&52e(?yv)Rg0gekc4GIvLg7qY$gNMdle z2@ZYb;<)U>#C(zzWQ%UMN8AP0D+vg`G=CZ5vkVfGiiB4`*cxf3uZHa}eQ*}8uC-!q zk82oLKewE`c3MR`Bs&ZHFZvBWF>a&k3b|NNBn`*&m0e;cuf?ZYUiY)pN$eg=OPBN%ezY^~z44Ks&2w6Ods$1+}vBUW8Kyj<_aM(R+w`S`%JgMS?q0VnJ;E zYrP-amDGIiCcT>~wYUUiWxLP)R8A<}b(poGNl}({xrziFKVE#ApLUGl zoZ(c&22lls4px;wiAyyTe)&S?2767zvJ^U#XwguK&OXLxizPaWh`CK$&;Y}B-Oh|A z#<2T*|CM;)9h62)(1)FW_g?m4aYoX|RvBCq z>&4b7a)o2vq+*YNS=jQf19J94j-r&saRFOp#jzu&3gME@CJEH9B$dCq%EcTqg zRpCIWVz5|E#QNimr|Pc+VRm!2jYS8NJmqx?<#9=+rDFGG8!Ngp+Tb zemHLO|IqKsU?qO)$#^BWZE}4YzEXAOBs{LKw9v=5=j*)+!6~xn5(W6x&fxISD5|*D|#DUX7N4i@uUf6-9jN#sWr`~ zQLmI=axa3-`na$?ep@v7bP7`V#&S|9`KV~pX~ehBBKZ~c9xQQLK9Y&@Io4voU8Q8C zoFfNnhW4ax4kNG|NOM)c0Q}BRHAar|j0~pp?VxHARf*x|g1z|JBz! z3HHnLOViVZa3yiFNHHSRzAa(6+YP_|1nX*JTSK`V2pN>8c696m9)?wei&4g;d8lF# zOJywzN6&*LW^_v?23LK!duq3_f6WD&%FYtqJ~8T6`TbcGbXA4rtsMVa>Z3yPZF4Tx z{HyP~1C39Pi#>`R{uD8fqV#Y&@6va-eCIo)HWyRv5T39R2JZD8qUxWZy^tAdXa0eZ z8S{&hGor?Cs>gN0#^f$ylOrdTNR|T;Gu%kl4eY$fF`GlDK$c$EV8_dE??Hq88whDW zmM^qCiocTA5H$SYk|_|Ae3lf^GYlux%R*+Lr?ZF}panGZgpFAF1=c?&?y|;2)Q}X# zgcm*`KQYR{%X5x3t%#ZNFb_P`3uNIFT@U&2r7=VrGpRi%VhT{>L~M+F)bO+jG|_7q zhSUIf4EiYl_&Qgl#_;P1Z@s{wuG(j$Myzw^CwjzmzkZ*rT;{k>*!kW6ukEl=<>c%L zV$zvedq!T(wyq{(1_tSp3Zx2|!#r|Ei%b*LddIhR{>vxO1i{u#HOd1>VKdt48vkg3M@2to9%d`xggcms8Lg~ajduQbIK<>T9f}iF3()WpL=?+TtLO}-7H_mE5!2al zo#2eRL1qgP6aF!%OaT%hu!6P5 z;V<8iecmSe+PVX~uN?dYUq^f@fn%smBqQ{ow|WO@Kf}qcBKjBDRnRwY1I~h!`i3sBnnfOe3Dm4N#)nq~?QQnB$rj!t2_E3$`}wFL&g1s`}@4 z(74fIl|3RCYbdiNDlh6QSS65CHxjhXSLrU}4u_%sZXfU=*^dCEOrSqm(=>M4TCmP zX4}rRo-5mJbk1ha#p;Mu9SAXfRf>brk^Z3mywuG2u90S4dgi;U!%)_XP;_bA-Qmq` z?B86oJQ8LGCPr{qg8DR5Xdl){Em1mDR%Yd)dDivLA1$qGm+{)?6okiWmAH6mWi(@V zvw_A`MxDn5ij5J+MLnL_m7^a_uZ~Egs|X! zLi#I#N~jnPm}wB!L}(w@mjSQOTXuxwFWxHcO`8oNlr2h6!6L02kjOwGehHU^couQQ z&{$Fui-cUGfCVZh%js&-I_|3pHWPPXJWyKqUjwkRecHPJEV~`Kp4L_yixg0+mN^~2 zrd_9S-boonY>}D!O7=s^_}9$rz}g{YVhz3Eb`dvo43l1B?7ac!qH1jeNoPBlGSNgI zS$AhfwLGoUIgmuln|_wF)yrtht5gC$DUY>SId2<`p?=1J#8R6t^G@Gjii%s>|GKSy z{`%op8LBc|*MFDE5iArXb9Y>!ay*#+44ud^C1_}vXLu6Whgv^XD|#wFHGB?!M# z;Zt~a)e|1BJSmM|6iwk?9ygO7Y}PSTvsLIhurkfb?OhiSjY^tsbGSejWE24vO`WBk zetx;hjt&&zUJ}FNQTApS-FTI$*39T^-MG*kuzr|%^CTAMj5%&=XY6rDIrYtK+ORI2 zuG3k_lw0*KHHt{~Dt<0&t%|;>ezN8Kab8Xd%#*C$9h2AM-Z(G}`QDx0W-?W9FCAoF z`&67wLo{XKLk0e%(wP!J%G*CLi}Q8{JFUN%%Q@P``5x8z0|`rIeJgPwvSp5OZcLuH zA7D^DZyinTL_J^iRPTtqD-Zs%nr#sa3R~T;v?89NaQwG836vG@8IT2_l@akfXYsM# zOEUz|sRH6Q)rHLlAEUXjF*GIHxQ6R4^dMb%9#l%B;!g!nPDB$9rPvQ*?vdG zw?ForXXkg7ZR7Zv;`T^UD%e7jLE9?0Yu9LO?Fuorm^z)EgF*7hMQ=gDTNxZ~`Y&C@8-ZqW>je!@b2ULY&xAo? z&y`+0k&U_SRt+g+2yIBBMw&L92`j8*Zrl|iN<6id$y}UDCi7rteQSGrZQyWM45zTH zb>@D6(>^b=o|Te^sZA9VvJQHeIJLIglu$)x`X1I^uSrT6gtDwI4YNW>!GvLXDuO&t zMFbCK>U;z;XGBZSmS954%#5NkT)8llaS1YQSU|uBlSBYqo&&vmBB}JJxv;v{!eFoX zT}c4^g(T8^$*ahr2DOxEBhiH(2?xospfb>Ni}IAw3Qw!Zr7fC|HbQ_P(!DUZ5U!9iqh?Vvv zqL*Gd--)uipiC#p$O=>bV{oF^SS(O+>)cDGS~IBAvxVQ>0RnR$f|rp!rRM}EKPiUg zEZ2{FsaovNUkSmrno@NHxY&<4I828VN$MboIZfZZ0IWjRV!4~q{Nq6MFQO5C^b=nA zw5+ocEXfHp=_+yjJmmH_DJk0ObYT~1&UUc38zVRjD#CCn@abaF3%|#0T54wa{i@YK zaoIu{CD7{0g_ncF|6%MLgENWSE+5;r%}FvbCw4N)#GE7(+qq-g<{jI%ZQHiZ&2DYg z?z>Ob*88cezjRlf?)vLG*ZFzayZ+$j7DPW9+8r3#nOQlA<#Khlb94kaa%gmQd3nc? z;L4TLvQXe$_Sj6hnO^+5NNq_JtJUX+@D7`IB*iMEm<;&=cQ(pr{0@rggF%j8Jl)MU ze6UU@_DmR>tS>{+ZCLonWBZ=pkj4npSCMoh?AB0a-!}?3x2rxJ&E_yn3I~Ug7!7V* zK?Zr|$6PiNFA~3Pm+%#~s+BszW zb2Ft!V86$4^ZM!c)Rm)r3CAid{F4}mlH7Y4^OQ2b1(r^!WPMy+m^Q|^%PsLqwNEoo zjv*Q@?aKt+tR^m@!^#cv2vO8S9ojQ$bc_OQmML+o`dj1Drb8oJR;P#R7>xdeQnjfs zJ*8^e6__E7hw~)Y?A$>D5B-Of0zSUEk#;c+=_1N~EU3KAP4qL3Oqm zfTmDUPJ>1B3kOmP+c*r4Uu22Z^alUWccP_cn9NASj;-+pzlZ<1-EN&@#*4PGc}5G6 z;vp23@lzp4xFbeKc_pMiSp|Ghvx61E?l|0O?9C-ys^ z>jy&6)dxoBIg_-!{HidFBd)LZd3P>|6bI5W-sJCNHBAg~0~nH3{bV5;K=tKHPXRvGBecfI?TGWoxs5|-9wNA3NrZ|d5=(`a&JB5~Cf*%1!J_e#x6h=8q zil9p)db2(kto7SH0{rsHNfe!}P;W3+5 z8Grd#5I=rBlaT!;x8G|#ibBV1@+peTzq(xF*(%Sw0P~UO zEViNVQyr4+BM>jz_&27msza*W;hREvr(oR`b$|Voo5{*E7vi2OqG2v7#>%B?jAWYo z?hgnzZ7st&^G}#!)a)ZJ!dg-?-$_=+{KCC7nR9!GT?gi1jZ_&~=b1l3IBtkzS3{Sq zdO|l;JLqG#?q_Zw6097s>3<>e0KM_1{Cy}qz&1Qd-#zg>(41$)MvszrM0i^^-g2&* zeDF?Yeu(9d}>!Twu%F9d_Sv)dv=SX z*75=?29E&`qjHialzyb;QJB@XS+Vdw_8k}tDVnohC(-)n@HW*&s{-+eYkP#+--(ozW)zgr^}qdaG~5i{?wf%F@P%r~a%{hV=016e zI$E-B#I&b3Qri?18c{EPV%@uwEJ1t35-xoOMdfgc_uUNS?R4t0ij=Ze$k5t{6Q8z!D-pVjgy9GESJ6Sx8q*mW$IlI*&I++h(iNgTn#d9+{D{L+uNwQ2tW=dO`W?p93K*b24TAYGq#@^+Z_@1Mb7 zNSXJ>0}r{tS&C?FYG1i{#l==)3+d>(uVjoulA!*LfXn<&a@EzgLAX%RsmX7nn(|k$ zw@cCI>Ha6B)m9j39lulL-rC3Yo%Q;Y{Bb%Y@1n$w+~F0)=LX1@we9Vb#IuWiOXx{E zbp!O970aV*%axzWu~@gaFt#z(i}dSdC#e$|zP;v&JuGDM@86#efA$4J9Lhj>9u1*t zNj;4>0v}vTT2V~$O-Ra^T@0}(%Ksc`H@pGG{m;!{tLqS)lQ897_Hi$gw(;`ED4O%K zL2u1jWU3EZ7ol7dXX#Bs24BvfQ*LO-K8@NJzx!Ci?&60hhF8>Ie@||#4+$)`@P=`m zu6x2BU3OD%qFcr^cUs@!L*UKDoQ<@r60u4OiW)HurQEO$FR<0ymE? zKGrj3xY(5IK%jLf=eGg8iS`|GF0D<9_}lWKZIyj@pl_*ig;&=SW+k~(*}M*ibh-_d z`$$InjY8I){zpdyMF#747}^rLkNmGl`}m+sLR;qRm_lD>NM4bTRUwD zXJLXl`)We++b?w6--Hk3rx7&habs6<+rMf?V<+w*c9hI{%zK^UU~eoHqYEbpOIN1w zo3Ly4yeLBt`wN<8{t(A%-O1Ar=RaO^eCQH7pv|ZaG#MYpQq)N|kUgpNNi8e!`ZMWU7tnp`_lvIVBJJu}Om0kT-!z+ihy^R|mu z^o`~+L1s5zVn-}K;w}D%^E3o9WwdllVXMM1oW{e?mT9x_*s42Mt(Fzzew5Zu)W4{} ze3N%Sma*Una!qD@rF+2wcT;hXx9#~Z-i-S;LA*@X8p}Bia{09TV!fnX4|j&#dYL^0 z5)GE?1MXu2!VJ<0=_+(9qjCIElWu0;=neTw-V%J`=HH`%8)j8>><&PXIWrjO|XiRsXUgzeobWn`v%ejdylXwL%X-yi%L|s&Z+vG*M>kW~2Of}B220fzD^BO|=KyE$ zH?p+>Ya{S;DrdH~z>U$)e&;&o@9Ze%6{~Y+XXv*0je*YI&e6`nsU!Lq$ai69&i5)4 zo7%o}#z&4fn@9IYB=_VGU&LDSa~k*Pj;W}cJw|oMj zOSc}n-Tdt_3Wr5EWCDR3Lqz|!46xdOp2;8VHrr>#@vSsMR66o%iZKA&+@Ez=2sJ?5 zbI?bUHz@ZI{_)3Jn_Bl}@^hPIf!xzlIXQgJa=j*JIm5nz2d4)1GBp)F#~n!esVdEy zceW$OvT}8G0hh;ehjuoZ@FF={o{DM`ZzWypsI`Gq@dKyLSLeSI)=k=V9f*kV2X$)D zzoy9QwZQUUXZVs#)hrEuny_>64%w=%Wh~&zS4fuUC&G`3WvZ!9Eq))!TdftUZa2*X z)&d@qdY9w1*|Ru(}b7TTT4A7Zs7_v!lz2-K2+xl7h6~3YHK;f^6(1AMoVh!6Pf->15c=G zs@}W1C*Hz~^E`crt{%>k8#T3qr$MUlah`M8|M{{{X<1C1eNQ&R9Od>lR+$am@c%40 zO%P0z+*M=M5C}-Qt~}4KO~Wn~?Ts-BNkd0b*vxtXc+}5O$f$Z6srLtk9}Dxo&}H7r zWgiU1dHcUhE48PMYn)r`Njh#z80`xLJK|7no{~nC78-cJ0AK^E-ofhXQRijeb=Sye zy}Na5ydI!#OOc>9Ru!2|NvdV7SdtBw{hCcSzbT&|oFV>>{Qb2EmI3qtq7R4tlqViv`ir4yVhVwv=5b;NeharZDf7B z17%9&5@<2eaMNfF6B>RtTH`LU;AnC*%_?RyuOlERn7`mUHcekvpmNy32@=g>FcATS zPWA5l4Q7kS8exV9ipk|JSh%jWALpDFs^X@PHWHOoIJ{VOoPnC14 zztTh9wp*k_Q$#Omkg-ggqNY{h88&*HDy3^idz$NKZ_=MauR`B{lzbvp(cB`PYqdF( zC@nE%T8i~P6%lon^p|$EOwgGlo2n>vHO&ZaTUqOb{v7VB=iwM(uekr2OvC7inS9tf zDqV$;@f+}<@~^-+yWH_g-s(zab<-zlFH7gtd4ef(T%P%$yu3cnEY?<|qzJ@Ry^LYH zaJqhWGplx%Ei5@f)q07p$Rg3iJdt1XUk%krP*?dM@*i&pK(Ok-3a|SJ+Eu2%6YD>> zH>_1`HanU1&U-KG|GfzmRy00G)Ur;bwI(e#mN&{T+1lt@)ugFvT2!pJFQ&~qN}UPF zwKY^2U3gi!aDcY;R({|$s8{N|;aV$OH0sQ@>hUTw>FV8j&i<@jY}E6XNndbGI?!8N zH-7*w7&9gfU)HVCU};tB|C05ZC#>$KVyG7E*E_n}Z94ngPJQv6m*>?ASFQ8AQcfcq zLR--BijPIe*YIP12-ZD>ng524-7HvO1%4ZNx@T?6*>jeB-ROes>8mS&fK`BTApW|v zu4YI1Df4+43h|-?1RwgicWMQ8oQG5iKo)IdWcVrD!pZm0a{cd{Yj2R0_b=;qV3&M` zy&1sCH(kO0ccnNJgUIX=VRl%B&-Erk?Yt4Ubixc!8y8#o1~*b@ zmwz|SW`NB+q^x+csk|3AL8tErGF}z?A6a0=rxCRKFpY1WY!j^}eg*hr1B|ifd7h2T z1@~^D`*+2{lfoYgnen0mdG~Uu2qFXW0P6_Bbp56JilP&hLVQ%Y;^QHYIGd2?fXe4G7o@N!1~Rk)aPwA+631XN(+m6eA&u{-dJ$`1q*m$jg%E9D>v9mx zz$w=E8Nf_rAs!|NL@i73nqP&=oA9-*LbkS9Uj8;3sn?Yj*zG~p)aaI5l$Wf1*G*M* z+FL5EJvV}|Vu4#|LJ>;tMVD+w?W}~crd$T1d%U8}%ChPpn8l%FW7Ry)A%r^t2@iqp z5j0@(Bp1#Gd*b!HR13#sFoGhi@H)TyjyYn$W}s?(HvJ)-ha-ep1x5*1^g00ZC_%9Q ze($JZB3+_n&e%S=P8qTE=>!>E1onrtZ$%8`8B35Mq1$p2u;wew{yIpU`eA@RpcyN# zC?0D`Hj@X94v#sgW_L$D+=b#wS)2OFR^s?ki zJxhJln7?cgzSfE9r5@aF_KJq~o40poIdqJcoDBrNqOw=4tcjrzh_ac-EQVCAV6l&Q zgkW6b16G0P3ILaCB{X`%56rkYVAte1o3JNg5OGAPA~pv`MB;Kx$kPsOPy|GF^IU&% zplBT|eyF^y{*>60_u_HLU*m{qn+B;@LRcz;UO{Sl;#9g^PDmcD=e)n%U!UBe&7qw& z_@mWJ;)w!8VrFRKDDVflLy{@oulvFbs1e})7R3hV@!Vw+PAx9OuLWgCY*z@dmPEgS zQH1Iceai~gk8;B-N!1FZv5!h*SWHdfCLu8hqR77EHki=>HIO(Qn10zCkMl?zv1o=egR z=BWI^&W}X@oPjOEE$>?09v>}a*OVKUYrM{Ay_DL=OL)j~g z>2*oDM0G35EEH?3B5sLd#!W;fl6qJP4-7T0mTjVD?{;$zUG%?j3M-LpGmZTCK9}2T0{JBZa z#umumJW41U%-J&@F!;xkcqn&W%Wrf_U3Dm8e!B8Xaw+AM``a_84Dif$=6PEWehO2> z(AhaT&A&LU{vNSv6reoU%~Y~>CO0$1&98e>;Y&07$N$Vm8HvhB@{6(3v(oV*JJ^X& zD1VUgp1>_&lbf6kdvWikuj>e#PokHK7do)6pssP$9f_-oKPKw1xa*S37ZtpDTXK3S zj-Pm$|7pS1)6f7d`H*(9?q{vZe}ZfC&pSOIsZ>fB>=ev*8k*0dn{pLgvB9*b=GdMQ zy|cUhlsu~|+ORd>CT6cN^W3AiGO^eXxTnn|?i9(VFODAh=%4%e#hWw!I&iw8m`UV# zIM_IyxxlHiEemuzL1$0m-pbDc43*U+)w!JB(~`JLtG%r~r-l~OE)kv(EwB~VsZnHP zP7CS523t#D6CdmhanH5=tzzxd;2!)6Lx_&BBV2r)*8~EnZ+P+0PtCcM&4!wPIe7u= zup9ss+pg;+6dzHtb*BP3n}6*YuY$sWxW=dSbHY&^N|)P`=(4OtWpZx6DAATvJn)}$IS>CR49pMr zkR~QI=Tj#IO^lrm++I7#`MnKI6tYyk6>(=W%U=@`wo`$YEuBnPKRid5N5(gQdM;;6A57ujQ%IKSTXBkz zP5dyIzOZH)a+J|_3a@kw8&4O0&7AmiW?U#ZbR_I@`?u8eA|iTHipwH+Rt0*}&UmA7 zI@sn}AuC<*vnk5TtlUedL(1xlI3}yd9?qLNiI$RCOLi`$ymZ{R@3-cImT(8Yt9dR}D^;!pA+x zZfS`sp21(z3VLM4(`vsqAX)%P^vJ$5JBRUS4eZ$E_fkJ^29Y+-P5C`NuaN>NW*jWCHZs(pZ9vBOs$4?ol7i}xLbt{=06V{`JoM5mc zK^TzvaB(2MiUqnt-F|`UC}v<}aDHKs@Yw+%cwv5FG-Q5o9E#KMk2HYTGxm|ox2zR8 z$!oxtCvfZS$diceB}>3$(&O!EoB|XK1g{tx2}<>`6zBH+&_W!H&yC2ZO>yNc?KVIk ze-X$HPcnkf={Y`r`XoRIn!O69f60D1$lW^_Y3Kzy{E8*yywsn%q_E|A`m_Q9rpw&& z`fAoByFNoT1$j&jO>^W_9+R`^FCm+tdg$_e`Xn*A2IIuAs5t=AzUy0Px^ zM-3E-&&MdXFF(HL*U;Nk;3tQs)_c0P?^yR9?N>Gn!`6yJ8=3ukG$vXwUx*516!k8M zY{67h1>|t;9Bf1I2-32U!%> zeVaiz#Y4(MipK+zUJ}JQp8T3Uqc3aX8p;C#Is{Z8z$PS?pvQy&9)0_lK|u5`Z9jrTCF%p?pqj)dQNfe;eB##Dd<(AmcFh^f zvte^-D5fEj#B)AKnhcro8Hv1AKF|Dhw{*Le@%^!5H$fA?6CjkJj zK$Jc+4B#D)?Fy3DzKi%=^uQZ_%pO#)Y=ZRSCem{By9&b7&glY|Jmk%vsP~%SVNwh; zF(&$O?ST1VyJwYzd05vW^xT9+5{{IJO5xS0Z_a{`1x~Pwq>sTlA+lktxy&#Tj<{~Oz!LS&=*Sti(15{M)4M`UyX&ndgY&oB}plMJUMHmRN*`7GSE z&NaRg1jV6PQ0%&e@CM{0yk2smcBqrkp=X0%nH>3MKpfSQe>{BQYBlCY-B9p7t zy$ae^oOd6g0{@|}(=1j=;C8@g`8*U$p%fFAM0$W4ZsYY2OYI{f+IK65gleJ)`8m2m zPoYajw$&tW{NpdD-iqHEwxQWl9+9{Aeb0nA=IYl3AC<#}7{;}H;e82h4`S;KRCh(V zdSH1)yy`(^4IJ;mB=YTO9yHiL5F7RdJj1qlGH2QJkQWPHQZ~4Dh8^kN!f<=St`Vy1 zXY^iYklcdDlwFqxd3T%ayX0d7G;ow1q?bX00#lSUny zgdmz~vK;})AFcoHDI5jW4fSGA4aLN@KOmI+IM@fhg)kFhikQZG#o`wgJc1drgPR=M z=wUoevRAOZx-s_x5sV|AsM;kyBt8J{Hr=^DE4*A$v{Bedb|!1blos45HPr_@Ry&qD zwmK#`M%^Qwbj-5P78Oj%G)ZqIX2oPBevUPCKQRBIPk~I{#sm{iilM(p2@6Ea5l&&| zW8|Z^ypiM+j~>{2fZ)T;BF*}pwUBkxK2QH(-T_s$a(3O?LA#m#WPXKn1ws69;z492 zcdGX0-SgDXBGH> zuVS`ZHq#HLA1pr}v__g*+N2G!ZMfD=;g-H7zuU8s*J0S9qG7+|EW9p|yZ!o|R6@XGx1ei|7@=dauo- z1=Uq&y|t>HMO%tlyigKah=&v}^iM4*f1~LAlzk6`Jt8wn&0rkhiw%p{rpnbSNik%eJ|+a}9=Grl zQiNb2&elBS-V#9ERn<{nI~u3SU56!N$!#bc2m2Fj51jW$XKzRwLLZBl8{nPy@AxP`{Ly|;`-nRdUcvxS+( z*W&5o?~t35iaM<;Y?jVW@V#J^Js{~|xLCxJ1Pjp%FI;bF2&`RuvP^VFuIXH_W`a-R zF{UhOohGNgXKit_|IWOvG#768o#NOUn-vJHy3zX*qbn&_O~oRJ9#kKX-%*)G(HYO*q$<8mXfD3TTaHumTSTO z-lVQ|Ww--IPqZe_Ki#j#KZ=07AJf>#o;Um0G9aC!7R8qJF3r_*BAqIMSzLM=a!`EKEY zTtTlLs>z^*AC3DbZKnR3Ua#?*c~5_WdxAadKg(HQBl#r#B z7^|JY5D!9pSj0jo^0~NBZC^>_SVHaa!Fzi4Rl#q4)sbw0cBk-Z_05&2AA0Y2H}IM` z=y;!{d0*(+8usbpR!zSHF50amE?pCz9lgEVNEF>bCO62Qw1$24^yJt8kQuhCf`hj$ znz60k&|ES0T8dbNH6r$#@eRZ7uHhL(!aa%Gk&3oW>eth!Rlne{r(HJuu%1cUeHM)f zR`YIfBxHq{w^3HF`;O;`d~ForHYJEZa0bnn@_^N7QGMThAkDO^ZRm)`qHd#lfw`H_ zcSQbE++)Ovesu$mS1O0;y%yMy)AZk2);*Ft&Jho~;!`G_&0V(C9>{{ZSDz>V&ji~5 zYBW<{)qBjv+^HI*RTA0!W6Uv9Ki%=X;{m=Xo;%oF66str^Lsy_U--4ceIRGtH~r_B zkWYal$E7B)T}W4SW7TKIjeG^Es}Hs|g@KfJS{VS_5l(eqzBdiMB3CaOOwU0OFT5KXPl19BV1?e%J3jM<^rp5Z zLqWdDt$N_#4%l4%d&_kC_!NTtK_Cblledopa3hXE9TWmMuQg|`GhJeS?_%UGQCtGDjy8|vZGrYc2R-|v=BsAoOOs3NSNM;_Pk-I) zu9@(A;Ju6dDCM;GjG!sJ)j>i=m~tK^#k0ObZuHI1u2}CN?=WE`VMz+J(ku5E=olV|3as^_jdlNX2g zrFYKvrgxY3r+4J{Xb-k8^e;B=tgpbY#4qUAWs?{9cbj*yPvHC2dzU``&)ESAWanO) zYbfqu)O)1+uPeVLeNuq^*7V2w#j#;kpm9Q9S_#MG}3M`Xcdz3#0D9&1u>;w~8W4YW_H`@W>xvFEp!gJa_I!=>Ed>L|VF ztQC=CV8xEj9`PRW5$CnSH1n|;AjnIqE7!Zof3eh*@f#=Z{-iemJi` z!=hIKf4Ys%o6UW37#^168&9RW@5ym)v0g2%S$7$FePp_9cux7bV{bN@-ozD*UFvlj z9DtV*X@5-jSu6_FT7Tq4^LOrqQUd{iJ+>)IuP zs`gf!FN>-!yX^^on-0(8%)Tokx2t28|H%i620kC}DF}R=@0Uz3+dxXyE3kDpxh=5EE`8-uxO#9S7y$%UNngvy*@}@BJ?AM@M3u;Amtu_c85hwGC zOCiH%a2!r&$7boiy#8THMzm0(3{g-S_pqtrBWbNcB@fnu0R zc;%g`YatB*A&}-3=QWXj+Wz6@w$F5Q_~)H)jeby*dfdhyecitn5;1Rx6yyM!;Iza? z_-i1Yx`?aUTo9uJYdV9qMG~{sw))~L_g&^7 z?|RbXmETRSGO5_vi?}`XPIla^?@F&3`Kn+u>(czR#QC$)veY-5hL-_eRq$EbI-ufu zRuIzPxdi9R*_vJ0>dVlr1$QpBtSkn!@vDk!n3qGv!|2rQlnO7JCp9Ao$Jf7R%n}bK zucx<%B1r7rC0!6u5HV}7GL{W=NQcEj?)=xrPqEzgm|tWycaj&ffmIZVIiEHe2utW` zAVn}K?FsFopHz+c0w5Rp39|>GE!BVwEQz5+hx>ceEKCq2f!I@qH%Kl<#Yr=IhIIz0 zc6Rpk?~j&0-`!FyS$awHK3`>}S|oco^8!~j@|f*0U*6KShJnIYQpOD*9*r$*9z?6j z0%kcIqGz6`m!SU;x$hC`zeq_VN)7Rf4n}pAQid5cD9G_rbgF0SUVavb`3GfzXG(as zvbAYQGzvIfbLU~Ix+A5&Lg~|R@aLPp$y?BfNQ?*kL>cow&O*7ka<8bm%HCNJKG7J^FlKp-dalWQs&rM$_h5j8Jq~TUMcRkWtm=GHCX86` zYL}=`tf}4PDyy9DRjx4of_1eErmcT7KHBOW#v$g+9BNansp*f*5_&JWg}E|yVMCCH zZ{AxToMII%GhSlSB29i68MZ!KD{Cz{F1j3Mi{TqQlQJ3IV7on7kKxz)nz zSFo1_VzCxq%h_q8EF!jy|ty%T`#U$El`E3(fau^JtmFJ!cxV>{|p!I;kUmf?c){Es`rn)@( zEgv5dTa`0A6+uI3+eN44PNcP^zi&J>o`fiNbPCdzrRsk@^A^!8dW2Ks%62OK2{Lkd zl92Z*EynGx=*#wkcIUMQ9Kksu<_-*aG-aVfPCRN{I@PmClT1Pmlxno`=<9ygDW75~ zGs?8|tT)8?G5R+P7zo6aK?oj#S5Mx4YTK|}4e3xGF6p-+-gR{KE#FUxCF)rliLF|LMI>1`1)n+xGY(JVBgszG<^ms#=VUSK3px$G!*=&l)t z>LTd-=!Ll?VBCef?MV4#ZWwl?tBa_kE<(*h=&^t`I1-q9nPI4E16&R)aYZ;I$_T8L zILf|b!3+T?y+Q#z5U`W+K}!WYt+UriX4*UyyV^vp7_uqwHS2Q8KJX6MI_2oz@o`*oA)ua?coH>-6tZ2Vx8jjiFD{L=%eWZDWc-S_PoSHNNk5irMSG8SkP|FIfadjG}J9v8z~GKajT>TYetpb+D5C*f=nyWUYm94AxZL35T>5M6sG|>8ralMS zESVU*!ipZxUpzeC;-ByD2QSe_HWORP%xfY;+ZqyQ!qV01)7Lon_7bU4w?EK+?jy5! z{*tvpnTtu$6p4yWt0QN(iJmVfEYaXsQs!5h^Ug0c&!=+8FARn!Se21==%1%Agf&vngTinhfw>-z-IJO{n##O%$fC;n z*?RT(bA+XhUK1a6B20Zx=8|FR;j-RTh9r#=pu1xSg(KbQ69FM&Rx46FQyWv>#vqc! zB$Grr;=$T9PQf|sbj#JES(z|)pv28wWFM2BMrI=XtFg&4t~OIG=9b}Lpf;*D!Ac~r zpiopk2l*#XOK1I6Lx<#6(8pf(m3q$3ZbK(kt|&Uy%>x-?Q0KIZB(s)Cll~T!hLNqv z-)b9wgYA%0tAKSq{{|?49=b2-f*eI}8dg7>jU6^hT%*2*2SZiK-=#k`dn2{~*!5d# z=2gvUW?ZUDpE;#;7!Y-NEPEy~wWWmnL_Di1td|oM=VJQcVgu4TL%0vMFLMLyvuh2o zF9vJVPIX2Bn(Evs4fJR9=1h^`plkHbzQO-?xloq5P?+$KayOQ_1jlmsw0F7GlI_+2 z06O|cH@*u)K$s!Gnb5&=iDfC=urfMwC)bcKb8vXlM}lB4HuGf;XUUso@XKb%vG*`Q zx97_?MrS(MqrSp}=x+SXBM}|t6fbF=Yh+PTP!+}b7xxRzVLkNeU{relEem?uutX;; zCgMRS++QuAoA;$n%24auGhJg(`zYzc+F>BopP!Gg4<+}JCB-XV3n}y1t?Smm-!2*p zqFy-TBmc%1)Vk|D^md(i`8w$hTt0xcEy17bss&b}T~XTe*A@@}7P9GS4%bSgLlLyG zuS!n-%}g+$wzLczwvl^rBJXo6=9rWZ(#ZICx6Y2uj;UiyXV8KaM`8CJe~dYF0n)c1Q6Dd~UtgBA^| zYaKCuZHE@^!=M#Kz-Wtg)_`gF%cqnj(a0`+L?Y|RXeaj-0? z`>%C9A7dQmOXB$vve;m6e13{oe;3S^Fx$;7N+)kEMJ-b;(e5@_^7NxP1urXM6&{?^ z4{Yl`%=tb;+kpA$EJr<~*k_%02un{~qVJ-0oEh%EZ_O81J8Y=fll&0Y0qHxc>P(-L z-9y*3859I6v!sqRPhkBb3O5#yi6M1EDDlDDKV#$hX8i+(JB;8vVvkmlvOSM_B5>L@ z`#+`CgSj?fw01xU{GaS5^-zikYRFH5<6`%nYkk%gh$!$!ux`m-e5H@635GUCi1)o= zyfJ=@6NU19ujrtuVUAACBZeEn6@MtNCr%B}YV5+R)gB{4##117kBRmiU zBztT4XUZx9ynZ04dBU$Fx^I){x@pkrSmK@mdc6UsXMETCK-G@7*>Jt7JxXD(?%B(O z&>LP*!4D~Qp3J*xVU?%D%5sg|$|iuV7jvg%hjM4BmpHI1teEVL>}VQwqL+QK`=NPo z?dE00`fG2PINdfw6B>8URfStQtuEGvE(qu-lN0ddrZ8WqtiXyQeueB8TD8ebsU z(UovfU5sEKV}a+flw~?p*|5cPgu6c8hDerZD@HEgrVv!zgEq(dFJn@U);^3^64w*i zQ!y_hxGxeHR5odA_*tr0d5EmJX*=7DMUsVRRPYHB7ZRX+Fr^Voek?~V@n!*ij*slN zu2n-5rSj~mY@?l8AIEGj@h)=C*@j<=tk7g)UfT!d?LANX*sp6_(ET*w7{$j1OGfi< z3n2w>@}?QHAF1U#o#gmtxClP5>z0~g-!C6bwrdHg*(jD9E!a39p-rN=c9XoZ$%bgT z0f3xPeAe$XsU8aKGl`m$WnG&}X+L9(i=1mmu3Ev4&MsWz*2MfaLLtuae39JeS(a_O zEh1CRr6O__^h3kilnRqF5GAQ2qhp2P_P}`SVDp0Q@&S%*1vQpHHXL?bgYbb><*~cr zU8th%-r)5gO3w4g&xjlSLfYS(OB7dvGlbv&3Ppg z5Z`e^{|nbRB@XXIzwUe6g_4wuB#B<1+=rVBef?aIKsZU z+tG~hfkq|YY{6F=iCkig^ayd0@pYa5bfYO4eu^a~BNofD;0?Ru_qXsCT7~zDNpcD4 z9sdF)=v(#`!x;X^e?YV4{F)MCg*F3#3V9*>u(HFIF4CCZem-SqqC;-fe}ONfLH0R- z6w-m*A%V~jN;Q6hJj#LG^ns#PpG!m8LL2y?oqD2>+@SghfeOu5fzU#O`cZ-IDgWt`u~7VzQDUbQ{W}9A&`s|8|3IL z$W95D>2@@H!3+NQ1txPv@nEnnKB9rPd30Aa>U(bl?s{1bd^ll;zn!dx<wA?>-gdleV~f!q(Wtm=cdg__)JPIkRTe#P_1PHg%2 zr!l!4lv0)&Rv-S9X)()+7d~}jMI%4SHDvw>asSAP{4G@A-Oi)?a!*oxjhb7aO=&`) zW?AlEd+g-UkzK%iHPBB)_~N~J!m`K2Nl(OBkRx^_ehOa;Sa4_4iM!-Taf>OjjXmwd zmQN9BkNFikO2p}jZRh)4hfK@CsJcBmR3E1*QP36w{ia}X*(!1-y|ZRlOL#-MepQk8 zHD6KA{eqqo;m3f3uc2=iKCUb#>%b((P{!npOUDe~563``Z3v6pcN>toMso`%Q1TG% zq}rloird0eP4p{H8(!n(R)LLB{242*JrDCjpGvSJu*ez9zxGI`$~51|u4fvx^H0@^ z@kB`Wmv6KDrRummphm)VFKcHu2;=O4A{gnjA961mdC;b_u{ zry%1o;vf!qAVc!p#=Toij50HNXh59WL7dvKMt)tGDasJJGYy7xLtf~+MQ}-x8kqYq zj3Vh*`xmJgq8j)n^=P;H22Mq9Jog15|q zs$rav=Cr?GS@dQ~W|o#R2@YI6d6{b&y^fofvme67C-jclI6)t#>bDBbRXnB+N+f z)NWa?r?6|$6ZpgVi&KwVnNwMC?#CR>8TtL)z5F?TJEweH9wQpS74R&h9PXF6TIOgk zR;XhO*!{RExk(kUGFS8px3<~UO4UxBUh~egt*Yj-XsG_ZOglWcFCWei6QiGFFBKC5 z;g*Cl^b)scACMoA;6q9KKNx$*C`+PlTd=AUm9}l$wryMIq;1=_ZC2V@Y1_8#tYqhR z`@SCU_3a=1Cw6R&wMWDlF;}d;*PH~dq^1P1@PzbgJ+Qv1-!_;|sC} zR|-q}>rTg$Wl4~w41d@mMXowz zI|rC|nmP>0%v+7itix5-XEqR2#um%>LY#-aQ!YvVgU+&KPHtP(BHWVpOB~j$+H?TT zm9G3bm3Mauf%U$rBJGySzY@J~u7HRhx9tAwr+Dm(AKQ9%cDioHBw)RR{z4?kXHvr7 z$J!d^qZsC1@jHow80>~z3S{DOv!Nl+Ht=k%8#X*JME&{~;9yv+3(`g0Cn#tnmYWu> za^5UFTn)B78?328qQfdZjfHgg^nOfaw~uUOlJW!%l^!#pH#4dC@c06Efy7nvO@h5_KU%yakZeuET-ylP`}Ilux_#i@hPAAOKYr!EF zCf)b95eLdYjo?-BS3yP$gY0rK(U?$9Tm{RpIjdHaTJd4=Pu~Ro)!e<~VzIzcfnq%0 z3MR%t2s3@~TgyPq0ZTi8fQ*omva&H2VzmU|U)|p53E4S9oQ@*Z-8}|ox(SKS;EWuc z%xVTB0qJfC*!~q1dP9mciRW02mZwqeKyhH7xUrC;E{BIy?oyR#cT8ET|Hc2kM( zbcVlqmm*$8JYKp)td|i4r&tF-;#iuqi;51&KvLEz=h{7;x^sXsS2FT>sijUiE^Myl zS^mH!g^WTL$_iv5Fps zVDD2*j0;MuR)HO|5}*cC zZczBo@cFumx-$@wRI2>Oe93`i!?khc9$8wPIQdM`l2)KvRpwWIUvVGtugQhvGw_qv zt+R@ObCssEbEI5F-o#e*cJ1mNZ#8{^>;q^LG!iN3yf>(mEnFn1JLdGd0}Dq9IYBWw zIWsY#Zlx=^xnk__aSP$aG`+Ypxf%PMmpuVNvpfSMzIJmwUmWyp5wcLw#8C<5kIp_; zMq=VkQ3wvHgkM^1+H!|aInV%|k~xtU6<0LTa+M^~q{4}YR2A%b%gv5RHL>lk$go8* zQyQ&V+>?UGv7|g&tb5Wm zXTycbzf{u?2Hf_finf8%RqN~?yYX+oX%afs8lq>8S$xd797zkccKIWE_I z`+0$Mp;IDQ{g!vo;59&A07L05Jy0BN;$RrAR{M6q9MXbarvgH>s%e2ny4)Gn@p* z4pT$zoZ{~aXeWy%wbZ=2j=}bZA6atjBrAnnilmfbry`mDi-{9p9u2E`@^x(>F2a9p z3Rwu2!BL>va1N#T1;m|P3{TCqL+OH)W)Uf9&CpJ3QFeTlqJtpi7XZ(V(ov;}AOw%I z$sz_ER+(f*N)Wk-(I0^{htgWKOJ#WjB!NHQB8g=5yCU4ALv0Sib$7yXARf_qNClDa zZo=NfR~;KTHS5G(axZpc&Mr9nvKlk*Qx9!rHj}(r?D{_P?YQiSK92qH@OeAtbPwA& zt^Gai*QA~kuO`iHp+kA}f3w!`#&6F$E=6d-4b^SLEN;+g&@%lt){u3CE*Fel$jmts ze$+##BTlep!PQB}BuvgnLAEv*3)x87gDxszs_Jl^caM`}4`T6y-~`d=7$8f)G0I(of3Ug1yU%&P+ak+Am^%~ZN+{>9E#&ev%r)2TYFpC;SWYi7dE|3- zu1rXTiO`tUhk}O&hfS0?KV5!mYoQgOV0|-}pQu51D1!3FLvsh5c|~?mI{aajAF+w> z+P+#F4vKUCc-^4miCZ6pOe#PJTEkxe2_OSI(~u=XY8<(7u5Lo4v6343UT*^)`_EH@ zHLQK#o_2y0=fE^HM!U0q1 zr&W%4&UadXaz{VMMn^Iw>&p8^<*WCpuU8mGryS>ejuNoJop+`;hqk7F%oS_06UD!D zZ__Clzf4X6yT>c@_VjhfmsAY9)0j^qvb&;2-#^+{%PuN=5N`}qOBZp`1XO&?%ve1x zh*F=GDWxL+DxKFat;M-PWBZ^Q1(xj}<6yu{tbj9QtMgZ~m^uPSuIRUpQS6*HQMK%} zc02}Jmn$^u1|Dm-Mn`h1Tdme5xG}ml{r2eflTt?H9XI03Po?wZoZK5)4|(P&^88LajPYMAdoWS&*>g&!mA&%l+jjcoOr) zlQR2rN)ZcZCi5QI5IP*As0#ou2J@oFoiQN;Q?M#b zq%os?OWOL@6s?ZH1#>3NM1uVyJ-(;ge0r?s276w&&jp)cegoD1P&NLG+rG>Yq%GR! z{M+IA+qP5x1Mj|#yUAsgJH5H{v#Q)hjmTTc0+$m0cS62SQX2VV`%lrp+H@8xmie|* zi8sS{b_{y*!~tt8;=*L9RP(vhD6-lib3^As-g&DQ?Fqe7=^?~q1cl{_jFHytpRqzW zc@t64#SU_f?o7c{y@jF^%H_n1?4Sz)t0;3+QcWe8CG%ucCi0lhz5oXXqAXL;Q}Q8+ zL6=QU>@|`%&GpqZ_*;AwbR!L1Vlm(d3fQQUDXCtvm+sxm!EqNhvOZadil;6;DQ+B2 z%J=bu(P^pf)N@)dw$bq6<1>#sg#WXQA(!Ep5`G{;BL?FeKmc2Gaigtakc$&^8 z<+k>Wb9rz60$^Tj*Z6oaJ})0ZcJF_)GwmKp4_L>w;D5c}4>2 z>a_uH4>>657TomCeCCfYD)ypx8L&3>eO=qKn=vej$a~P`>`wYa1WRaJo%jdU4BKJXg}{ z9F5Q-jp%(cf#1U(6uj|}p;M@oVR||@S8EX}1N2-9gb`0(my3s1h0|=}$yCP@NxIsr z!SGMCqDQWS0lBC4roqU5`x1gS$w0lbX&4FB@ygl6F{3(4(zys)e7o4aNsZXfsswkF zF1b9jTls}tQ3>)3-!z(>mfY0X+<%zl3#pF$>22MA8$SsP@@3{sFIbFRYu!hx1m(^|?fhr1O0J#YU& zzzg&m{!HIxek*_JS)XeAssmn3sQ~b3ogTw`hIpBLT0OPdV~`EAbbUlRUPttpn60Qp9LpiRt(4(X^UH=Mu{(gC1dJuoOVJ{ zMf~PQP-<~9n)R8SjQTNYq%yzJt+^yR%AzHML72OZXzS1cR6ZDnQW>8v%7=XF7wv`wo+uXY?$ zY!ALGDq|ntmCoIMoT>Tk^P5t-Kc4x1$8H2)2|j5-`bup(y-RSjYzxtg*NvyFacp+i zUAy$2>W+yZ(}Z2|lk`Va!a)KV5)>zdgN;!=`g;>)4(N|SgK3EtNmPeoba%!g8b1~y zj+lt5Hm;&kme2|2$>E94)g1oRkJESH!;C!4K%}#Nq>VhIBlr4L#8Umap3$XgW`uot zuNw(413TcB(x%U6^K6Ry3rEzcY*t6{sd|r(s6sOyaq==>lnn6^xDJzo=M!E`Z{7A) z65M8%DQV;7Hn(j<6e%KFY2Hz>XRqeoMX{hAu`2zO|cQ8RR zLRzEXQfyulo^$7Q^7ZY0Z11(jQDkQ@@p|hVwQjg)tSgxv(#e)jd$X9DUL7UDwsq** zt4uZaYA6a)kRY}7N%x{oG(&P#`ZY#5R*yYFjOG1S12efJq9RpF$Ra=xEMK5xqrgJR zQezZg)cT`puR^HEamUQ_*p1o^+YOH2Gwr(II^?=F{C449ZrsjLSw_nM2k0O!qsx$< zW=hst>+V_qU?QCjk9-#3d3}x<2_8~$ms37?7E8s+s7oQ6HqwmXN@Xq4W}p1&aqk$3C zOpB>kkO&E$TjtUN9Ybcuq|c_-6~rsT9+_|Sw<;Zro&%V0lW?2+G0^sXh$h=+<6E5A7__^ce`XFBRZJF07^ z=V#{T=OXSe7jC6gRu)8^?(pvF{#k zfM-BN=Gm9`FO^M+(=z0fgkX+htSXdA@52}RACa8HRoG;5UrWx-{lDGO; z&2s;BJarsD)z;<8MJnsg4Xun@(TtSJ9oc=CH0QaZ03IhoSrS+HBA^!opm}H{czF_+ zqZH%VwunVYX!t|kt;3RiRdOrzDRfvI;0cKG?#{6=tciXG`wrILP~bHsif0?pM( z!JG0RE_U>*hKltGoNs>br*XB*Qje;gkVC{|jmkYzGJmkV19JF6O=G)cJ;|c1l|?A` zYqOF%O?SXc*an)OMW>5=zeWP zg`27x*$cRMXiYo(`A3~1B5r3KI#p9uQ9;vg=C&?3RQ|9#6E_BFjURECeAthqG$c-) znuUL`V?NT`9~THc+NjWiJcsPyA>bh(mz%&Yg?+d|quCV|lNg;E8+*JoXqz;ry}3RL zH^#ZCynzBCUk+``Xdygl!_2C_I%$@;dHM9Cqy9-f`9oDGyyH?bCQv;Z_eB;3Vl z&Qa_)=?cW2^!&;C3dUs}Uo`a`>ga+t5V z_jbFc#exCGX!|5jGn1_PX(AV@x~;Fwo@W;%@j#!RQ_*hu+H`N`++4yKlBTa5zo#|V zad|Ag)OfUX2WH))GX#dBW<;VErIYuIx{XT(Sa*Y#xY&nF6skggA~||ok4$x{vTDMc zEkJ&gc$k>1%rgIg9EDu%&=5NBfs!RfsO%}=WeKE5z2~S*3uQTa2Y;GQuFr#m)k4_TmFS=k%8_)`zN9(IubU(&VQ=pwSOY<8 zbS)VP#^qAZ8g1vkL&YH0w_a|2sFCKgpk2>2E5KILlxcraLY9888XqWgPOgwi8%a(Z z(I^1muaRJ=6f-4``DLeR$aq+Whm(pWE)9s`Kw#G8sZ=<-k&-3Nw@0qxN1l9BWi;b2 z;N3gNWJUH**5X5aiNO5UBOl7ww^_L%1*v!Tec_qCHpbV^_PzW=FUQ~hIbq(69r_0% zQGq|i?lgbSs<_z`Nyz7wfGKIoVhFh?+^ND0u8s9E1ZEL??qf{eG?$7``jzF)@|3f9 zW2)G`Wh}+PvW+%tVGm_+E+^ffg&Vmur7T$ybL+}TX{zNKsu9wz|g5L3Y4!mXWm^nR?^V^%|*}g4@S~^N7?~@ zPuvNP19@dbT52L)cgze#kRmVl-M?lM{Z#dUh8351&ZCA1Y)4;8Mct?4SDSRt_1_Ks z*0o-354ws4W=?A*o)7)3ma+KyZ#VWf7i);b$2eVv0FFBcjj;jAjU;-bJ(QfzyOTet z)Umjx+&B^)vJ%|*CJB}knU}zqN_n$xy$qz+m@!{Vy-jq-%nTi&&|zu!xk6JEKPs7eaFx3hRk_p3*Cx^` zz{Hi>t4H#vm`?WRc;$)%M6)F^d_7FV2Wsk|AEgRZzjKh(9i+rvDoVGy`nq~IcaCD) zr(35xI9qrty^Sjz8&_*F7pW@LNvJ$mNxFI7mCOF&vzi0?xC(MxOvHSw9x~??5U>+v zd&!Ed#!Qolu?lo*1;*Cl`3XXom5eJ9| zkhaiZJo6HT)VDLL_eeV4ZaY~#wnoSOLgPW2&xgYm`7MFoq@6aVh!UKl6vKv*hKYy7 zdx64HOj!7Nov&s6{GMxnvg0(;fH*&{&!iz;cIzcCHyR^ugP)OeHTMLzVozpo+c$ZH zWdNdtBb#YfA5^fsUL_QKS_kEt)`4q3S!>lDGnsR)mxRQ^8^n87*8iTbd^%^zrKfrx zK&!u8FnUhju6Y5%Hya&mlfxWa`R4mYgfLnd8xJU*MFB8!tm}TW;5fVYe5!M^C-Hr# zm~L}likW83rUI^;ZSd2HUzg`K4eZ=-rQ5!xfN{8b4F~>C9+z$5Od(Sm%xIr(x#+(E z4_MH}G%E;tg07g8xyH+OvC8~MQMH3;H*+Ywmfgyh(T8Gb@RPGH9s_T!wpzNhqS5bs zu1ys%i_s**3sTpC&1uqmp0u|$Q|pDQu*`7uSvnlWK)qdD(MTwCRBIO2jHGAuOQ(6k zJA(-NINUSMtm8WKeG@Zh*OBqjyIV}KH4fHNP&r%rv(@>+=OpS(iBCAWk#D;3gZwyV zg`Hc0cq^dm^p9oWG7S2$gNiBqEoyNZFQFMUE$~r@m03w5qN`qKND@RU)kTTno zc-s%vwDDJP!@vTpsZ+)8JK1msAqN+7*3iEL%DY2`)r=MYJ%F66>$lXQ8YVkUm*M^c9i!mfQk{p z`_Itk&@ZdZ7qL9Mn?BQz$cp-_O)W;V2T$8BR(+c#hIsas>h31yoGk{R-Anab4QKb6 zC|#OgReRrI-AZI4RLAf8$i!@|!b#WlMdjS=9o9ik9eRSV4CW2Z;(MxBfEko~VnpdA z;QjT-tBUA5NE~>mP&n28OHc##t+153YhwNwV63$k-!* zoIPm5At!aTO9Y^l_IQz7l1AdDV=N3_9Ee_YEnZQjMtZZ50cgLj)^yP3v7-Det z6iL_hx31wMD69^>%G(ERo>a4Xv0H7*kYU8IVJsUO5rdf4{ikHtn>I{;2YVx5X2^yp%WFDZlNPmE`0i_TLNov~h>xXk=sG?le3kSO@ z0Y40y>VQJ=1@q8%g-k@o+K!{Cwv(vnk=vro)nh@VuehJS)(>m0yD(yURJYjfUDz;6 z$|%E%{>($5SEvkyd73kAj3V6;U8)HBNaW5!jS|5&sTG&kVb_~?Pu2OBqPd5Yk{_{F zXo#N;7^oZV-JKqE&f!3PHn*`YAY(i9V4d#3X9T9vy0}x_?PD(N@&ooe5Aj!C{ zfIDVB01t!7SsU>!_ViTGfV7J*B1PtCo*zA$LUP`*Mu6`(NgyB@Mi0Zhq#!;|c64?o+(((G3Id)lY{z!g1}T#FYJ|YZ^ecU^3|zQjwta zO#4hXgL|ZD>|x$P-f7aI_FeU(>zdKO&5Bqb07B{c%gkSsCz3xUjp^Z_NWlXNM6Nl> zY$SNn2WDhTCVTG==m87WuKCBd7LbmN$`81?^Tb&1(igHyETqMB41C5sAKjk{6m*#+w}f*B z)B2=Rf~r#TS&}w+W9#jcq6-PL`C{4P0m4|}3IMVrcA0l7<@g|LdDFuND^J5`oAl8m=j*YZ>W5qqfVqe>Ysp|K62NfCV;x z5tyboH8nMueY9OpM;AQkt_iNB)3l7cZ;~ztZ|ua|>mV)HC>c8dS&p!xQ=9CcW!Dy2 zP6tuQqPFWy&dx6PGgIUZI|lWczu3|h0PC7_l0xK4%ImQY@l#4%{vXd+NplWd^o?uP zuGnSy3j8wi`>Q$O>)PTa*k*2KFK1)h1hywz%$>*X%g;7b=kB$smWKv`ta@+`Vfi|? z6wjxfeA(pm?4fRV4qjY=as-@YLJoIhu;76^qs;C+?u}t6=8+1D-+PRrIYjv*@w!RM zNg4;#QkSYKdP(|8>MAHy^z^heG>`uhn3xizEJ@sDNsL1kJ|)Uyyc1F7!$dA`H5M!& zHNplH1n_31%m^$ws2uBjJFeqN0y(DcC6uDs2$`E{2_);(DU#t#D;gzUAHSn*K4IIV zhrE_O2A0j|42F8OUE7-2N>ykdy4=4!p?f{1xiYNV8>-@22ONH{I8D0gq)4Edfr|1k zAh-iMcuN;%P&m6cGp6on*JVd(dRjMkjQ^=y)k59VW9>&u8_@w|)*Gyeb8CLoey45> zwj=13EAN?dq3abo=37?YyXcVWl?)P>>=t4VwZ+#8K?PQ*Tq|KpIo`VD^J&ab(=H+L zW(+0VF{S1w)fIJSD(g#WC~eW|g_6pQ$+}om8itFf6C@p&GmbK@?n^sOBu*LDFdv0z z=xw2J*69L_I_7>0VbQ!qFy7V$z5kou77F#0b)eA|Vi#%`Sp-`__`oU(e1kkCf>XlZ z6u5#(YsAp~8cXw)bW?YW1(x;kci?Cl`)y!uB>@LM9Vcv}$!|#vd61JGtJve;Yu9R6 zTcgjpwg=yuNbpEr5Ws3vsz{WH{-(-E${8sX;5Al>i}XpEbYzg$5f}vi=+r+t^VAA9#Je&Z-u^>9M7zRTexFF$Q_XAmm^m6uW)@L4dJ@{fZu%#Tr z@ZTb8t;WN4zaz85R>%Ih8ZF;wIo<^+8%*UXJEUY^bJTL`PIZUxE|+P|bF-`Gx^cbW zC2`(Q{v0P6epy<~b>HL85?c(O?6SBb&wfek=ykDaA%CHqE(MlZAx^w~U1jK1LvsEZ zR4en%r(dFX5#KIkKM7Lqlgzm3_SE;SqxL!G|4ehR^(&FGDeE=-vo0H*%zg*+ z!!A{IEqHBxx5zKKysnSy=iVd86{sUv6yw5&w#9kGPGyLa52s;;RC`W_@aw;R>NhFv zGez#Q;OeGskz>n4{k(5OSNGu`-oviAYwU~&(Qq&pjoz-@! zb~ns#_9G?+)R~~_5yO3nonCpn`!+;7sK!c$#jeOc zo9e6T+rhqsd%uSFrTUTk#wKper(j~|StWge*lqaZ6Fc|!X?UnhpY#5xcUr1q8k@b9 zF>c>6j1{0X9Qsdx-+s_s8f#r7EQCH&UFJvAO@KeIRpliv4m|8d!(e9#4nOtF zJ~&)m)wUINd$+MPR%;+H9*~dWS!c%{1bPC$)xrSVF%l1CeN|$|8ibpMM-B~v8}`Bo zcaIFDV>QdI$Dfym=yT?;-V)+_vA?$vU?yoF03TF?&GDD4&rEyPN9(=7pId|HodZb3 zagnD@@O~VW?H9O<4$%?TEwv*1aEsCQxR*8@W*izE`hCUw-<$ql zl>xrUE}#0t)F2QvkKen`5Wi>r!`MBpsIyX$I~clU^OFoLZ-N<5PKyhYapU+ z?j)@Etjzz|*ZS#^!$FdVlIM*x12dcaRahh<%p5-18~=+JEb(5&j}2OTYa3%7#JA9D zKp;^hI|L1EB`1h2GA~fBS}vdyiUvzN#Mvxc%~P_H3TGNQbs(m<8T`R?t(K(`Csfa+ zU(0A+Pzuj!c1Hp%g+`(y*Fy_b60AIp?1ufkxJZJ3hCz7N$ z6mVtxYik@yL2;Vrq$CmU1Qqfro?I3ss<W_4UF?E8852G&P&lWcsCHa3ajKdov35Mfg&LN=swL-G z0g^`Ke0&D44RvQv?XId!NlxOy^9_jxSiDNU)@gMsle~7f!SJ}7qfO*f18L^#UoTYb zr5fHWp~T=p<&Jp&a4%-d$0kcEa(r7;7x>sVHN9~gAp@{3g3w#u8FBNc|;s9zE){$GnW?D zrW!`03i<0nhqO?W$o5!npM0PwKrI*#BKf5g;~XAiJ=t|ttwujWHTb{f##YqF4AJ25rSR4)0takG`oERp3vCOC@ zSZgJg(AHWHHe&#-KW(JM-lSBfnZ*`$O0YB<7X0Oryp{3r6FJmUqk$~LX_U6!IBhMh zU|fqoy!-8~#AM9xou6FinokcOd5cE-ImQ;9vFtq=0-xAOit;|?L7anjF&){nic5EB zD0@9%o^B*T66>Ovvigfx5Ca`iVq#{IMLBYekn0R8f3MJSEbj-*F=}xEo6(P8A#c7v zO1&{ph`A+Wu4#AZ-Vq*n`BeGJxHAS%1#|8me-yow#gZ8h7bwJ?3fb5!|C~N*HpBCT z7&m#dDKZzOCnHk}PTqW<70Cd)_qY81d9OKbR7h_L&%vxDdELn9GMB_!arjOXucG|S zGgvKKy(P`GipU*s-UJ>v!FEf7s%1t1@_Qez+U zRrMsNXa%(mAIq45t&HrG-btk|$>st{+C&_G=}|h*4Q2(ho@*=XXM3dtB+ZzNmkBD&w0ru$9yN};!4bYCaBSwN953>7CR8)rkbJX{=+B%g-Se<3Z_z` znzKS&i@scC=K57R+RCC!Awhm0$=OAetwLSVTxBMs==q#lp{2K2WNcx5U0uzkTnz5V|)m{XYmn^>ERZU@WX4Icm zm7SFMSCFK}s@rQ_uC*YjLh5tz@_x~~`Z0gsFdQp63@;Tsam8+w6eRALrA`Xv} ziSAus%a>X!(S9m9Fk`Aooh-NkRWWmd$D^XPkmO(^suVFp+FGEcSR9OMUaT^hayrLE zsZ4A37n|aDl6FNNf+*F&yanP)vZ$OuitDj)o$!&p64GMa)Tu)KvLFs70+gR0ye zp+qvhv!vv)FCuACN?Nu{YDV2$ku(L%j&0raFRzwQenlWqp+QBZ8r-@vP0GI(s|Lsd zUL`Rum0v-FB5)}sx>#MkuT-I-K#j_jQ}rV=O5=nSs6|cy6;XWIsrQEE*YH5(v`<|R0w1glikt}vqRumV4_ zccrJKMlyphq^c)4%*wm4hWO8zZJkAqvPME)eG$qzkq(t`Jt71wBcGj7-J#fm5Tv{< zOWiUF$3G>U}K#wBWMO4?|jt21U{iJ<+zO?;& zSMkC6wyj}8>Ey!n{FQ7{N$XusJ13Dte1zeMV~^wDg|+1Ar=3qd5oIqo0lAIRZ3b{U zWPp9oxUC5bh|O(+4%mEB4UJy4*3??g3fQUh-82Qjc~T`8T_P{C>$>!wa(tQ1MX%5L z-n8OQoN{=-K0wCm+WjQlv1dR>l0)VIsRbPN>tkvSs_&uWZ}BqJa=vB4k%;sKw*v79{xxNrzX}e5%LY zptw4_)@>oSRPbUjKPN?~2mJ(%6=YNY4){_=Epor@hMw-*&S6uouW>c5-rHB)>Lqf3 ze=SLuu+_dQ%ijS1h0{gPFUJ~Y%jeIYAMpWcx^8qd@RJ+X;_acNDTWO1N@qH_CVD(g zPc{~pLqpuxGsLBs(ETIRz0IkZx%VqzK7YGI(pdF`M9a8vekoiWSHHtye+i5$if@cv zJ0X3MQuoXs?chT$BDUOh=nQKM?Vqme;q$++o@J_STi1E}FWiCZ_iw3Ad9A}YTyTCq^Xb_!d~bc7jCYwuSm5I5IOo(Uub)3UjX@S%YeGmYwNpqnD)Ow z!9Ck>+tS``_y6=!h`iI@UOK&AkKw~1`$~Ip>tyxplkKT}N`U^8ln~C%qZSwrWdAnd z>Wui^Eld|56KX?bb9jA)2D-uY?ldn)499zA^b^gdP&|CAx4`-#J1DTKHrmI}F6@#0 zgpKI{rvG;uSNgbS&*WMRDxxq<*VL=*64|vprce9YaC1}BKW!M(-#O&h8$eq}%tar{q33?LxF{pn*;_L{lN_W4z5?}0_@HB1;HScr-dj|y=Yf`nbS#%?HFV1kLfZBJ+ zFvCP&imRn$k>e9`P5SJ@bMxlx#FN_U1+qU);CG*~*?HPu{RXdNMz5-94dJ_Zm}^_M z`H_=nwRnQSeqRrj;W>1?=O=xTv{9zmf4~LS-2gBshN#>-WUPwtU;NtBR?7Bx+;pFq z5^6?JQi-4YMVTn!5SnBgW>G+^B$f_lYKC2aLzyZfUbKg4-XN|8T>#SF@k;Tjvk7lM z(3s=`RXqE1F-hJeNa~n#_9y>&l21Y>!MJ6@^=a7~MI2FeEnJzIl`77uHQd>rH^$E? zTu=NC8=8%p5NutQ!DDpwz(S_~@gBaPE(3wBkp&bF4*|W3yPYWky}XfyvXk}y!x+uT z%E(DTFJ^A#Wa{u;S{XW-ikKSPntYFx`+htB>2h#%B4A?qKk2v!H8dTO#Swg_Yfr@Q z^ETT6Y@&*$`xrWq1HptO_NRiwMD!9`qFRQL`nk4d#k@{GN<`Wi){NWV&OJTU?2Jf2 zCFO(?mPkFybmZLekw>>0$|?gt?F+ zvYKd8^Dt~^K4{R;O5&U!!ek^E@)ClGaAsfxKYhSR%4sQgsJ~PEgF^J6q_`-G2{LvC z1sm_32n^x85KLi+CvUz}s`fH8#kJy6Cf@HJCAxZQBJsi{B;+zmy*M)>8UBA2~Gm za8cBIX?1h8(MGK9EpyNX~VtE6%w=nc)pf{;e-StnqmAgO4(hkH@LU%ave3I&%C6hH|g-$Bp=C@@)Nk$hn<`R*D2;$T$$1H z;m^OI`w>XGCiQi4dd?=#IFV~St`|4v#H-2Asg8RuBs=d4SY3y%h20G?W=!KDEs4>y z)cGu}iE6+qps<@66%!z$YISqjam49ZWWk>u`6sqn($0flb-kA#iR1;i`uvM~Jiu7o4=qP!lR;%FHuQ-b^kBt?bC9#N;%MwYnc- zB|%o}?K;h==5JKMI$WFBD%mCrot>)K$gZawDen=}w5AHR4oHWRYgGG0_NFCold zNe}(IGhaZmEmx;sFxRVHa=k23;O9&g02`83)Ii1NyLOj9jvtOk_ zG}bUdkvok>vfr47dm9g=ZYa-X#eLplSu;pI@PUA7xm;sdZ&LJ*=A5F_4EHRGkigSH zG|gR)K~N%|As_N~Vss&JM<=js*V!Cs8*iab>4(^!cxvIP~c%Sup8=s%+5dYJ@rA1%wrCsIX z>1jQZeb={@v8Ba#<8uNd(YLMZyH#8!1JM?ywjlL9D%Ze>cs|5avUBs{uR(a4+7imHm%-1yP~tIat& z%}w&`0Stt5#SaIvLQoG@={$x)PzN?(X^<+@rj((ZTICmRa#jHXHDF|q$1VD@$d*+O$_bvpylnQTNIX8dMNaX}$9 zvF9E`n123zARjK7SqUTOHe%)OQEnG?^dGXC#=^=ff*~f+-FF6||I6qF(ic(OdUzsH zib|zH!t@~Mc|*Xhh#)`fQSL;Tw&S4>RFE#StZyXR2w3w!ANnbrOQ){9*6?yiY|2S( zZEpO`>mQDYb-C_FGoOT8jg zI=L)Bzq%?w&yCLzDR~_`U&&n^bhLp8+x#t&{xt5k6v4YRc0Le^FXTB!(Q?@B#bDbt z1`>_Oun;O_t8ugQe|2|OL2-TA9>!gRCb%_DXr!T$4j!BU4M78q6Wjw01P>4_XwU#b zgL~ud?iLydA$X8rL5IwLW^Ua(SKjZbI&Y^=)qYsDSAE~w`=>KmFjmDRJm_ixeO&;0NyIw?S+;m5!%5a%^n95FA2TkIoGE1E0^h~FP{l!-k%;y+ymzZDK1F?Q6)ER5 z@+9vB#Xu+AI2Xo3f&Zwp)VnqVj*Vu6LEnelV%HQHe33b@<@IELT)6JMnfg*rQmt3> zvOaDbGIm3_V?nZ#$}9DG6iaOF-A@#mR8pNuINnAc9_IlHvc^gh1K;!jbyb8>3hSzX zWoNoPWheM!>^gTq?BqJ_+S0%h*o*Guk;-^QUQL!D$RS&e8+vb2N3nk&+!Vb(n!cHo zO$f%o2{$hK3EP6c%)OB1|GnQWzu>_(M-xa**%y7l(PIVWcsS$qkW%i$(u$#yZ*}1v zl;4(twK*0q$|*yZ6+KD&j*Up@bDQLwFU+n>38p{KpOiE32kYh`X-wu38#L84>*TIL$bKk6L^Z z%aJx^dlANx&g^Uny@1kMHdDj#h`t@}ZsU)Q`o76$jYh@&)(I9Z1%T(20Qam(Vo9P? zZq}x$#pdx+AxxGC6HSIKOwJGJZR@2QEjXe&r#PHGwZGXpoYjyv7PrdIjd2EvGaUdM zx(>K-&y;N+5BLQxl0Rpezgk$~)OfmEya;t{h6ZDwO!}qZK>IzWlJkWP-3cL@a6vjP zuR;``1c|S_iN-0GfsK#K!sK6x znLiM8g%o_;RNX<(^kBT+ORZTOByk+zdF=Cz7IcEivU@EfuIv0sX=byUhf4pBFM*j! zLE(2r>WPzpYg+AFng+#*xXvJB_wR-qttH4Ie_Kt5@iJ~?d=G0_y-N7ZfYiL&X|tHG z+pN2WVjJ;mh0v>FSm5g6W=YnF5*+$@J_L1^(nWxL8)<1)#=>CQ1N{tyK?dNfZ3&xbzt6 zD4GRVd$Qa&4{1eLqILs&CYLMkz#;@rXJYP*@qH7G`7mPY zgi<=?L|;e$Q_nJ0H9!Xd@Cix^MfZ+nWXrt6`5L1nkesa2T(d)z=?ey%{QFza-(&|2`>bA4s0y>?+9tNE8X>cZMMNf4xe= z%k{OUms!|>H6|KWg!W`0DzHeidJMQiZSA{D2ZW~Pa_?N85}oCyFlw7a`4{n>bwTH* zH?$C75sl~w{Oi%eN6R)*k#m3)BG*-YQ+laY6Z5Lfz4}exH*Qr*E==O~8Vr>|tl^a+ zWnJmEB~HihpxRcg?M(W07wI687^XI6We!JujoI3h zZr~^^wCDwMgN&2&B}&W&_bxhRb5MjF|6BSP^dGt(1bl%HQU_yXn4VY06GxSBdBsoc3jPft1Xd~Z zP^xLM=jlO9yqJU27|#2sxs)6}eCq7kYX`}GNR2RAA%p!9o@T#QrxyM2;7QRGa&$A; z*x{NJ9M2g0?zGi9Dpmb`qr^=aHJ(K-dq$@UzYih9M*x=7+Q3oubiafTFE+TPnzz1& zN&Z0UIJFQmc7%;*B+o9%@k##ai4rg4!ij#GwHSgizKU(H688*ccCH_m+U5)s&9SPA z#G-Kv*QM}mJdptJ7!8@eHtcqqDuy2|DdOWBP3&JDOYz39Au`Cjk<9WO9~8QJ>p^)p z4;4ukQNu-s8C?_f_d?~M;H&?8p|Wu5Di#5&@_Sbux^UgJ_64-KjO-Qvl=gH9hove( zx=X2sP|or7F&DoVTlB9W@PrOdWi+Ac=h@dHKEy4C^4jUki^yr4O|3AC%#nj+N&_M< zK!_k+*IeQdbsT-^4yd(CBO31SCJ28u&$1yGBs|gC_zvzX4Rh+gLM@7KmjU?QH3sD2U(KWa zfG4>=+A=KhilHA}sb$WLGoX|q-R}B9ReL_d_S6DG`hg&wzJ5EflF1Fcp~MQTv;fX$ zZ?z&m8Po!fWN2+(w-z_=c7y{HjMNnTUG1Trxs?9fC7PKx9gz%r$F8Ld#+Bxrx5Pzn zP@y44ot%OD{!MyXA(QwaMw|IWOza>Z=7CMtJE=GZoD+MD#dvtv(2bSM&%uj89zo7~ z`VW@*SPKVx8=e#SGq%CUcaee;-p&#{V3)1POLQN3h-)##M`*y`FZ9O? za-*FKDMo@@_$LnM$G?_qe;7l2kuMDEj(=qq**_vkg}29Mv~A7DnBH{g&&I;6#B)V_ zZQ3Go>hTB|8Cta`i>D2OEDO^F#9r7Q`aa91lN*zGNUJ`tC8qBznpQ2g>chw==3#*N zZQe#gyNTkyqFlMToaqxE>h%Adv9D~^2;m#-Y&znigwa408jCAIt|G<<8;uYL#dK0H+M5o zf{Jn7-E$`S7@TQdvD1$kVqGd#?V$xHi3F$1{MP05Y`l|LEX@1Z>{79*=IR|QlW8-S zD8$>KSq|pxt8jcmqvUOPVN!OeWAI5a=lv5hOf~W|4Y#QUOCnL=4| zwvEK>uOM!!dyl9O^#Ldq$#^i1FjSMnZbZ`O!1xd|Tv65a&edd0PdwlPG;(Gnj40w@h;teqa%L^#Xsr5zv#$6 zSjGP*I`SX~{!$A6g^v7L>Oa^2ztIsO81$dBDi1Q2W0pZAVTZTmO?)Jw)>=}Uj7ZvY zL!Jq2n{*URt?Zp^^nBI|wWTYwn3vqx(w>2|igs3_v_$vdfhO(W7fEApdph0U`XX(Y$?oJu!NJd18BU6?_1>O(~|%j9{e` zEAyr1^vjbLj@F}_k~p!uN$_1LP-C4?h;CU4Y@q)d){@EX%}p_gwD($!Og zOhW?8DQJ>6?}uBl&ZiWcTyg!#Ss`;;62ZfruW@8GxQh>IS}9$%!Dy1YF--WrVg&u{ zV>gCpaxMhNyOquaD)ZVJDEYhD*i`Cd=sZE66o-9WNiA}9KL483lG#4ruPzdb@uy~$ zcSu-`(XxCw@7_%q08+|Te*D$+sZ#*@0_o@Af@KHux+!KHwCp0@rEr)&lww3@xx~JC z2D7}(GuKvBQcHZ&H7tWeASY8w++4Y6NwN|psYb@$MOq&_Ko{>?rOA|67T zv{Tdm@i?g&zby@@F3OdaD-^c&U5;OmRJ#iybubu6c!Ys%Zua9_cF5=2$GtYwTl|s= zIwOocObjfL*Kgd~2b8!g9&$`5ib`mKfhb)z<29p{mNtZDa3_L!_x&=h(}C@bCmgIo+$xu9o-u6 z5k~?H?;y5i!oFlLX0cY_nVoO^UN>Cmf-#!X8+^XCemQJh|3D#$J1=;?#*dg_Qs1k1 z9+53vL9;?1b`}D}p?P#^^S(sIx{iIE@vP({rJr_M8p9J`Vl2kE#qLCAA2XRV1dggT z=@%!dXj+dXZzxPu+6$MxeU0y!m=Jq2ra1k}823t+p|%K`KJYM1Nq@u*!k%l7=Q^BW zpBhSTO{3PK^WOaKZ0gWteihK*TUkZ?y0W-wuc1d&dpu2-wd=g=#&STzzafEpD1tL5I@3B>L8u9FyS>D% zT1%K-u*?)Pi>qsw?NmnX?Q|7By;u6j|#R#;eKEv-Cr$dY+=c->-I-j1D~ z<7ce)wOQfZ^$d*&g@+TQc36l$l0~lzPXX6*36bMe#$+$}C9;270NsVpUh-5eaiAjN z7gVIf9wt)}x1+ie&XCW`023#n>W_+0-|B1e_T&s@W3gAZwm()T_z~&Iku_+-9Y2rK z`&&?5ak50r;qVB$bURmL;h@B7tMhsp>!cp5Ib~3bhBhGOJmB(wgxKMFXVF`Bh_bcrf+wa-+4wR z1kkm@S`1FCc3zs3)xqfoUQ^ZlHX^Br&+S5f&iU^Xlg zFh+0KrD}<3j`|JAmyj)CG;8MHY3Vqh`k*9DZv5J zZsquzhfnl^JY}v7XvuW-MqoVsQB2`|t|2Ty_Gi)huQzHYM~Xr~@kguAErXTf2MYAL zeh+~sYNtH--IzTN&Z;r#^B%oD=NrJnN}Zp&{^;(IycILBaLF1rO4(9R0)lt!LZQ(E z6a<|e{2d|O0LqD(7V)K5Js2r;#QCT^W|~Fq{`tEuq$s;;XFn6c=bw&$FqHCd2jea+ zR2ZF0yy?x`Gk&F5L(Jc)VY(R@Nv%f_mm)$(NnPn{Ze#Wy^UG}QFxQ6^r>V>l7wXs9 zP5Bs0&__I18zc9Wh0b+B;^G?{u%1K5PT$<45>T2L?8(&Yukz1?NPKk9RWqb}?Nkj? zUn@iFd>IIpS#NSFUJ3SPpBNFbeL GET some-bidder-domain.com/usersync-url?redirectUri=www.prebid-domain.com%2Fsetuid%3Fbidder%3Dsomebidder%26uid%3D%24UID - -This example endpoint would URL-decode the `redirectUri` param to get `www.prebid-domain.com/setuid?bidder=somebidder&uid=$UID`. -It would then replace the `$UID` macro with the user's ID from their cookie. Supposing this user's ID was "132", -it would then return a redirect to `www.prebid-domain.com/setuid?bidder=somebidder&uid=132`. - -Prebid Server would then save this ID mapping of `somebidder: 132` under the cookie at `prebid-domain.com`. - -When the client then calls `www.prebid-domain.com/openrtb2/auction`, the ID for `somebidder` will be available in the Cookie. -Prebid Server will then stick this into `request.user.buyeruid` in the OpenRTB request it sends to `somebidder`'s Bidder. diff --git a/docs/developers/currency-converter.md b/docs/developers/currency-converter.md deleted file mode 100644 index 64f770608bd..00000000000 --- a/docs/developers/currency-converter.md +++ /dev/null @@ -1,56 +0,0 @@ -**For the time being, currency conversion is not enabled, feature is still under dev (check #280).** - -# Currency Converter Mechanics - -Prebid server supports currency conversions when receiving bids. - -## Default currency - -The default currency is `USD`. It means that any bids coming without an explicit currency will be interpreted as being `USD`. - -## Setup - -By default, the currency converter uses https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json for currency conversion. This data is updated every 24 hours on prebid.org side. -By default, currency conversions are updated from the endpoint every 30 minutes in prebid server. - -Default configuration: -``` -v.SetDefault("currency_converter.fetch_url", "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json") -v.SetDefault("currency_converter.fetch_interval_seconds", 1800) // 30 minutes -``` - -This configuration can be changed: -- currency_converter.fetch_url can be any URL exposing currency using the following JSON schema: - ``` - { - "dataAsOf":"2018-09-12", - "conversions":{ - "USD":{ - "GBP":0.77208 - }, - "GBP":{ - "USD":1.2952 - } - } - } - ``` -- currency_converter.fetch_interval_seconds can be anything from 0 to max int. - **The currency conversion mechanism can be disable by setting it to 0, in this case, there will be no currency conversions at all and all bidders will need to provide bids as `USD`** - - ## Examples - - Here are couple examples showing the logic behind the currency converter: - -| Bidder bid price | Currency | Rate to USD | Rate converter is active | Converted bid price (USD) | Valid bid | -| :--------------- | :------------ |:--------------| :------------------------| :-------------------------|:----------| -| 1 | USD | 1 | YES | 1 | YES | -| 1 | N/A | 1 | YES | 1 | YES | -| 1 | USD | 1 | NO | 1 | YES | -| 1 | EUR | 1.13 | YES | 1.13 | YES | -| 1 | EUR | N/A | YES | N/A | NO | -| 1 | EUR | 1.13 | NO | N/A | NO | - -## Debug - -A dedicated endpoint will allow you to see what's happening within the currency converter. -See [currency rates endpoint](../endpoints/currency_rates.md) for more details. diff --git a/docs/developers/default-request.md b/docs/developers/default-request.md deleted file mode 100644 index f071d91bad6..00000000000 --- a/docs/developers/default-request.md +++ /dev/null @@ -1,44 +0,0 @@ -# Server Based Global Default Request - -This allows a default stored request to be defined that allows the server to set up some defaults for all incoming requests. A request specified stored request will override these defaults, and of course any options specified directly in the stored request override both. The default stored request is only read on server startup, it is meant as an installation static default rather than a dynamic tuning option. - -A common use case is to "hard code" aliases into the server. This saves having to specify them on all incoming requests, and/or on all stored requests. To help support automation and alias discovery we can flag that any aliases found in the file be added to the bidder info endpoints. - -## Config Options - -Three config options are exposed to support this feature. -``` -default_request: - type: "file" - file: - name : /path/to/aliases.json - alias_info : false -``` - -The `filename` option is the path/filename of a JSON file containing the default stored request JSON as documented in the [openrtb2 docs](../endpoints/openrtb2/auction.md) and [stored request docs](stored-request.md) -``` -{ - "tmax": "", - "regs": { - "ext": { - "gdpr": 1 - } - }, - "ext": { - "prebid": { - "aliases": { - "districtm": "appnexus" - } - } - } -} -``` -This will be JSON merged into the incoming requests at the top level. These will be used as fallbacks which can be overridden by both Stored Requests _and_ the incoming HTTP request payload. - -The `info` option determines if the aliased bidders will be exposed on the `/info` endpoints. If true the alias name will be added to the list returned by -`/info/bidders` and the info JSON for the core bidder will be copied into `/info/bidder/{biddername}` with the addition of the field -`"alias_of": "{coreBidder}"` to indicate that it is an aliases, and of which core bidder. Turning the info support on may be useful for hosts -that want to support automation around the `/info` endpoints that will include the predefined aliases. This config option may be deprecated in a future -version to promote a consistency in the endpoint functionality, depending on the perceived need for the option. - - diff --git a/docs/developers/features.md b/docs/developers/features.md new file mode 100644 index 00000000000..b9bb9053ed5 --- /dev/null +++ b/docs/developers/features.md @@ -0,0 +1,12 @@ +# Features + +Prebid Server documentation has been moved to the prebid.org website: + +- [Adding a new bidder](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html) +- [Adding a new analytics module](https://docs.prebid.org/prebid-server/developers/pbs-build-an-analytics-adapter.html) +- [Currency](https://docs.prebid.org/prebid-server/features/pbs-currency.html) +- [Prebid Server and GDPR](https://docs.google.com/document/d/1g0zAYc_EfqyilKD8N2qQ47uz0hdahY-t8vfb-vxZL5w/edit#heading=h.8zebax5ncz0t) +- [Prebid and TCF2](https://docs.google.com/document/d/1fBRaodKifv1pYsWY3ia-9K96VHUjd8kKvxZlOsozm8E/edit#heading=h.hlpacpauqwkx) +- [Prebid Server User ID Sync](https://docs.prebid.org/prebid-server/developers/pbs-cookie-sync.html) +- [Cookie Sync](https://docs.prebid.org/prebid-server/developers/pbs-cookie-sync.html) +- [Default Request](https://docs.prebid.org/prebid-server/features/pbs-default-request.html) diff --git a/docs/developers/gdpr.md b/docs/developers/gdpr.md deleted file mode 100644 index 8da2e917623..00000000000 --- a/docs/developers/gdpr.md +++ /dev/null @@ -1,31 +0,0 @@ -# GDPR Mechanics - -Within the framework of [GDPR](https://www.gdpreu.org/), Prebid Server behaves like a [data processor](https://www.gdpreu.org/the-regulation/key-concepts/data-controllers-and-processors/). -[Cookie syncs](./cookie-syncs.md) save the user ID for each Bidder in the cookie, and each Bidder's ID is sent back to that Bidder during the [auction](../endpoints/openrtb2/auction.md). -Prebid Server does not use this ID for any other reason. - -## IDs during Auction - -The [`/openrtb2/auction`](../endpoints/openrtb2/auction.md#gdpr) endpoint accepts `user.regs.gdpr` and `user.ext.consent` fields, -[as recommended by the IAB](https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf). - -## IDs during Cookie Syncs - -The [`POST /cookie_sync`](../endpoints/cookieSync.md) endpoint accepts `gdpr` and `gdpr_consent` properties in the request body. - -If the Prebid Server host company does not have consent to read/write cookies, `/cookie_sync` will return an empty response with no syncs. -Otherwise, it will return a response limited to syncs for Bidders that have consent to read/write cookies. -This limitation is in place for performance reasons; it results in fewer syncs called on the page, and their -sync endpoints will almost certainly read from the cookie anyway. - -The [`/setuid`](../endpoints/setuid.md) endpoint accepts `gdpr` and `gdpr_consent` query params. This endpoint -will no-op if the Prebid Server host company does not have consent to read/write cookies. - -## Handling the params - -For all endpoints, `gdpr` should be `1` if GDPR is in effect, `0` if not, and omitted if the caller isn't sure. -`gdpr_consent` should be an [unpadded base64-URL](https://tools.ietf.org/html/rfc4648#page-7) encoded [Vendor Consent String](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-). - -`gdpr_consent` is required if `gdpr` is `1` and ignored if `gdpr` is `0`. If `gdpr` is omitted, the Prebid Server -host company can decide whether it behaves like a `1` or `0` through the [app configuration](./configuration.md). -Callers are encouraged to send the `gdpr_consent` param if `gdpr` is omitted. diff --git a/docs/developers/stored-requests.md b/docs/developers/stored-requests.md index 8b7177160c3..9adf4ed1309 100644 --- a/docs/developers/stored-requests.md +++ b/docs/developers/stored-requests.md @@ -1,6 +1,8 @@ # Stored Requests -This document gives a technical overview of the Stored Requests feature. +See https://docs.prebid.org/prebid-server/features/pbs-storedreqs.html + +This document gives a technical overview of the Stored Requests feature in PBS-Go. Docs outlining the motivation and uses will be added sometime in the future. diff --git a/docs/endpoints.md b/docs/endpoints.md new file mode 100644 index 00000000000..88116144a41 --- /dev/null +++ b/docs/endpoints.md @@ -0,0 +1 @@ +Endpoint documentation has been moved to prebid.org: [https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html) diff --git a/docs/endpoints/bidders/params.md b/docs/endpoints/bidders/params.md deleted file mode 100644 index ebe0401c2a5..00000000000 --- a/docs/endpoints/bidders/params.md +++ /dev/null @@ -1,24 +0,0 @@ -## GET /bidders/params - -This endpoint gets information about all the custom bidders params that Prebid Server supports. - -### Returns - -A JSON object whose keys are bidder codes, and values are Draft 4 JSON schemas which describe that bidders' params. - -For example: - -``` -{ - "appnexus": { /* A json-schema describing AppNexus' bidder params */ }, - "rubicon": { /* A json-schema describing Rubicon's bidder params */ } - ... all other bidders will have similar keys & values here ... -} -``` - -The exact contents of the json-schema values can be found [here](../../../static/bidder-params). - -### See also - -- [JSON schema homepage](http://json-schema.org/specification-links.html#draft-4) -- [Understanding JSON schema](https://spacetelescope.github.io/understanding-json-schema/) diff --git a/docs/endpoints/cookieSync.md b/docs/endpoints/cookieSync.md deleted file mode 100644 index 2378aaa1cdc..00000000000 --- a/docs/endpoints/cookieSync.md +++ /dev/null @@ -1,55 +0,0 @@ -# Starting Cookie Syncs - -This endpoint is used during cookie syncs. For technical details, see the -[Cookie Sync developer docs](../developers/cookie-syncs.md). - -## POST /cookie_sync - -### Sample Request -This returns a set of URLs to enable cookie syncs across bidders. (See Prebid.js documentation?) The request -must supply a JSON object to define the list of bidders that may need to be synced. - -``` -{ - "bidders": ["appnexus", "rubicon"], - "gdpr": 1, - "gdpr_consent": "BONV8oqONXwgmADACHENAO7pqzAAppY", - "limit": 2 -} -``` - -`bidders` is optional. If present, it limits the endpoint to return syncs for bidders defined in the list. - -`gdpr` is optional. It should be 1 if GDPR is in effect, 0 if not, and omitted if the caller is unsure. - -`gdpr_consent` is required if `gdpr` is `1`, and optional otherwise. If present, it should be an [unpadded base64-URL](https://tools.ietf.org/html/rfc4648#page-7) encoded [Vendor Consent String](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-). - -If `gdpr` is omitted, callers are still encouraged to send `gdpr_consent` if they have it. -Depending on how the Prebid Server host company has configured their servers, they may or may not require it for cookie syncs. - -`limit` is optional. If present and greater than zero, it will limit the number of syncs returned to `limit`, dropping some syncs to -get the count down to limit if more would otherwise have been returned. This is to facilitate clients not overloading a user with syncs -the first time they are encountered. - -If the `bidders` field is an empty list, it will not supply any syncs. If the `bidders` field is omitted completely, it will attempt -to sync all bidders. - -### Sample Response - -This will return a JSON object that will allow the client to request cookie syncs with bidders that still need to be synced: - -``` -{ - "status": "ok", - "bidder_status": [ - { - "bidder": "appnexus", - "usersync": { - "url": "someurl.com", - "type": "redirect", - "supportCORS": false - } - } - ] -} -``` diff --git a/docs/endpoints/currency_rates.md b/docs/endpoints/currency_rates.md deleted file mode 100644 index 537713b147e..00000000000 --- a/docs/endpoints/currency_rates.md +++ /dev/null @@ -1,111 +0,0 @@ -## `GET /currency/rates` - -This endpoint exposes active currency rate converter information in the server. -Information are: -- `info.active`: true if currency converter is active -- `info.source`: URL from which rates are fetched -- `info.fetchingIntervalNs`: Fetching interval from source in nanoseconds -- `info.lastUpdated`: Datetime when the rates where updated -- `info.rates`: Internal rates values - -### Sample responses -#### Rate converter active -```json -{ - "active": true, - "info": { - "source": "https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json", - "fetchingIntervalNs": 60000000000, - "lastUpdated": "2019-03-02T14:18:41.221063+01:00", - "rates": { - "GBP": { - "AUD": 1.8611576401, - "BGN": 2.2750325703, - "BRL": 5.0061650847, - "CAD": 1.7414619393, - "CHF": 1.3217708915, - "CNY": 8.8791178113, - "CZK": 29.8203982877, - "DKK": 8.6791596873, - "EUR": 1.163223525, - "GBP": 1, - "HKD": 10.3927042621, - "HRK": 8.645077238, - "HUF": 367.6484273218, - "IDR": 18689.5123766983, - "ILS": 4.8077191513, - "INR": 93.8663223525, - "ISK": 158.0820770519, - "JPY": 148.1365159129, - "KRW": 1491.3921459148, - "MXN": 25.5839382096, - "MYR": 5.394332775, - "NOK": 11.3144425833, - "NZD": 1.9374651033, - "PHP": 68.6139028476, - "PLN": 5.0130281035, - "RON": 5.5172855016, - "RUB": 87.2333891681, - "SEK": 12.2141959799, - "SGD": 1.7908989391, - "THB": 42.0074911595, - "TRY": 7.1224176438, - "USD": 1.3240973385, - "ZAR": 18.7774520752 - }, - "USD": { - "AUD": 1.4056048493, - "BGN": 1.7181762277, - "BRL": 3.7808134938, - "CAD": 1.3152068875, - "CHF": 0.9982429939, - "CNY": 6.705789335, - "CZK": 22.5213036985, - "DKK": 6.554774664, - "EUR": 0.8785030308, - "GBP": 0.7552314855, - "HKD": 7.8488974787, - "HRK": 6.5290345252, - "HUF": 277.6596679259, - "IDR": 14114.9081964333, - "ILS": 3.6309408767, - "INR": 70.8908020733, - "ISK": 119.3885618905, - "JPY": 111.8773609769, - "KRW": 1126.3463058948, - "MXN": 19.3217956602, - "MYR": 4.0739699552, - "NOK": 8.5450232803, - "NZD": 1.4632346482, - "PHP": 51.8193797769, - "PLN": 3.7859966617, - "RON": 4.1668277256, - "RUB": 65.8814020908, - "SEK": 9.2245453747, - "SGD": 1.3525432663, - "THB": 31.7253799526, - "TRY": 5.3790740578, - "USD": 1, - "ZAR": 14.1813230256 - } - } - } -} -``` - -#### Rate converter set with constant rates -```json -{ - "active": true, - "source": "", - "fetchingIntervalNs": 0, - "lastUpdated": "0001-01-01T00:00:00Z" -} -``` - -#### Rate converter not set -```json -{ - "active": false -} -``` \ No newline at end of file diff --git a/docs/endpoints/info/bidders.md b/docs/endpoints/info/bidders.md deleted file mode 100644 index 7c6ce960479..00000000000 --- a/docs/endpoints/info/bidders.md +++ /dev/null @@ -1,23 +0,0 @@ -# Prebid Server Bidder List - -## `GET /info/bidders` - -This endpoint returns a list of Bidders supported by Prebid Server. -These are the core values allowed to be used as `request.imp[i].ext.{bidder}` -keys in [Auction](../openrtb2/auction.md) requests. - -For detailed info about a specific Bidder, use [`/info/bidders/{bidderName}`](./bidders/bidderName.md) - -### Sample Response - -This endpoint returns JSON like: - -``` -[ - "appnexus", - "audienceNetwork", - "pubmatic", - "rubicon", - "other-bidders-here" -] -``` diff --git a/docs/endpoints/info/bidders/bidderName.md b/docs/endpoints/info/bidders/bidderName.md deleted file mode 100644 index cd525e640f6..00000000000 --- a/docs/endpoints/info/bidders/bidderName.md +++ /dev/null @@ -1,43 +0,0 @@ -# Prebid Server Bidders - -## `GET /info/bidders/{bidderName}` - -This endpoint returns some metadata about the Bidder whose name is `{bidderName}`. -Legal values for `{bidderName}` can be retrieved from the [/info/bidders](../bidders.md) endpoint. - -### Sample Response - -This endpoint returns JSON like: - -``` -{ - "maintainer": { - "email": "info@prebid.org" - }, - "capabilities": { - "app": { - "mediaTypes": [ - "banner", - "native" - ] - }, - "site": { - "mediaTypes": [ - "banner", - "video", - "native" - ] - } - } -} -``` - -The fields hold the following information: - -- `maintainer.email`: A contact email for the Bidder's maintainer. In general, Bidder bugs should be logged as [issues](https://github.com/prebid/prebid-server/issues)... but this contact email may be useful in case of emergency. -- `capabilities.app.mediaTypes`: A list of media types this Bidder supports from Mobile Apps. -- `capabilities.site.mediaTypes`: A list of media types this Bidder supports from Web pages. - -If `capabilities.app` or `capabilities.site` do not exist, then this Bidder does not support that platform. -OpenRTB Requests which define a `request.app` or `request.site` property will fail if a -`request.imp[i].ext.{bidderName}` exists for a Bidder which doesn't support them. diff --git a/docs/endpoints/openrtb2/amp.md b/docs/endpoints/openrtb2/amp.md deleted file mode 100644 index 16fa451ef36..00000000000 --- a/docs/endpoints/openrtb2/amp.md +++ /dev/null @@ -1,127 +0,0 @@ -# Prebid Server AMP Endpoint - -This document describes the behavior of the Prebid Server AMP endpoint in detail. -For a User's Guide, see the [AMP feature docs](http://prebid.org/dev-docs/show-prebid-ads-on-amp-pages.html). - -## `GET /openrtb2/amp?tag_id={ID}` - -The `tag_id` ID must reference a [Stored BidRequest](../../developers/stored-requests.md#stored-bidrequests). -For a thorough description of BidRequest JSON, see the [/openrtb2/auction](./auction.md) docs. - -To be compatible with AMP, this endpoint behaves slightly different from normal `/openrtb2/auction` requests. - -1. The Stored `request.imp` data must have exactly one element. -2. `request.imp[0].secure` will be always be set to `1`, because AMP requires all content to be `https`. -3. AMP query params will overwrite parts of your Stored Request. For details, see the Query Params section. - -### Request - -Valid Stored Requests for AMP pages must contain an `imp` array with exactly one element. It is not necessary to include a `tmax` field in the Stored Request, as Prebid Server will always use the smaller of the AMP default timeout (1000ms) and the value passed via the `timeoutMillis` field of the `amp-ad.rtc-config`. - -An example Stored Request is given below: - -``` -{ - "id": "some-request-id", - "site": { - "page": "prebid.org" - }, - "ext": { - "prebid": { - "targeting": { - "pricegranularity": { // This is equivalent to the deprecated "pricegranularity": "medium" - "precision": 2, - "ranges": [{ - "max": 20.00, - "increment": 0.10 - }] - } - } - } - }, - "imp": [ - { - "id": "some-impression-id", - "banner": {}, // The sizes are defined is set by your AMP tag query params - "ext": { - "appnexus": { - // Insert parameters here - }, - "rubicon": { - // Insert parameters here - } - } - } - ] -} -``` - -### Response - -A sample response payload looks like this: - -``` -{ - "targeting": { - "hb_bidder": "appnexus", - "hb_bidder_appnexus": "appnexus", - "hb_cache_id": "420d7329-30e8-4c4e-8eaa-fe937172e4e0", - "hb_cache_id_appnexus": "420d7329-30e8-4c4e-8eaa-fe937172e4e0", - "hb_pb": "0.50", - "hb_pb_appnexus": "0.50", - "hb_size": "300x250", - "hb_size_appnexus": "300x250" - } - "errors": { - "openx":[ - { - "code": 1, - "message": "The request exceeded the timeout allocated" - } - ] - } -} -``` - -In [the typical AMP setup](http://prebid.org/dev-docs/show-prebid-ads-on-amp-pages.html), -these targeting params will be sent to DFP. - -Note that "errors" will only appear if there were any errors generated. They are identical to the "errors" field in the response.ext of the OpenRTB endpoint. - -### Query Parameters - -This endpoint supports the following query parameters: - -1. `h` - `amp-ad` `height` -2. `w` - `amp-ad` `width` -3. `oh` - `amp-ad` `data-override-height` -4. `ow` - `amp-ad` `data-override-width` -5. `ms` - `amp-ad` `data-multi-size` -6. `curl` - the canonical URL of the page -7. `timeout` - the publisher-specified timeout for the RTC callout - - A configuration option `amp_timeout_adjustment_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. `debug` - When set to `1`, the response will contain extra info for debugging. - -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). - -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](../../developers/configuration.md) their deploy to reduce this timeout for technical reasons. -4. `debug` will be used to set `request.test`, causing the `response.debug` to have extra debugging info in it. - -### Resolving Sizes - -We strive to return ads with sizes which are valid for the `amp-ad` on your page. This logic intends to -track the logic used by `doubleclick` when resolving sizes used to fetch ads from their ad server. - -Specifically: - -1. If `ow` and `oh` exist, `request.imp[0].banner.format` will be a single element with `w: ow` and `h: oh` -2. If `ow` and `h` exist, `request.imp[0].banner.format` will be a single element with `w: ow` and `h: h` -3. If `oh` and `w` exist, `request.imp[0].banner.format` will be a single element with `w: w` and `h: oh` -4. If `ms` exists, `request.imp[0].banner.format` will contain an element for every size it uses. -5. If `w` and `h` exist, `request.imp[0].banner.format` will be a single element with `w: w` and `h: h` -6. If `w` _or_ `h` exist, it will be used to override _one_ of the dimensions inside each element of `request.imp[0].banner.format` -7. If none of these exist then the Stored Request values for `request.imp[0].banner.format` will be used without modification. diff --git a/docs/endpoints/openrtb2/auction.md b/docs/endpoints/openrtb2/auction.md deleted file mode 100644 index b532923e793..00000000000 --- a/docs/endpoints/openrtb2/auction.md +++ /dev/null @@ -1,789 +0,0 @@ -# Prebid Server Auction Endpoint - -This document describes the behavior of the Prebid Server auction endpoint, including: - -- Request/response formats -- OpenRTB extensions -- Debugging and performance tips -- How user syncing works -- Departures from OpenRTB - -## `POST /openrtb2/auction` - -This endpoint runs an auction with the given OpenRTB 2.5 bid request. - -### Sample request - -This is a sample OpenRTB 2.5 bid request for a Xandr (formerly AppNexus) test placement. Please note, the Xandr Ad Server will only -respond with a bid if the "test" field is set to 1. - -``` -{ - "id": "some-request-id", - "test": 1, - "site": { - "page": "prebid.org" - }, - "imp": [{ - "id": "some-impression-id", - "banner": { - "format": [{ - "w": 600, - "h": 500 - }, { - "w": 300, - "h": 600 - }] - }, - "ext": { - "appnexus": { - "placementId": 12883451 - } - } - }], - "tmax": 500 -} -``` - -Additional examples can be found in [endpoints/openrtb2/sample-requests/valid-whole](../../../endpoints/openrtb2/sample-requests/valid-whole). - -### Sample Response - -This endpoint will respond with either: - -- An OpenRTB 2.5 bid response, or -- HTTP 400 if the request is malformed, or -- HTTP 503 if the account or app specified in the request is blacklisted - -This is the corresponding response to the above sample OpenRTB 2.5 bid request, with the `ext.debug` field removed and the `seatbid.bid.adm` field simplified. - -``` -{ - "id": "some-request-id", - "seatbid": [{ - "seat": "appnexus", - "bid": [{ - "id": "145556724130495288", - "impid": "some-impression-id", - "price": 0.01, - "adm": "", - "adid": "107987536", - "adomain": [ - "appnexus.com" - ], - "iurl": "https://nym1-ib.adnxs.com/cr?id=107987536", - "cid": "3532", - "crid": "107987536", - "w": 600, - "h": 500, - "ext": { - "prebid": { - "type": "banner", - "video": { - "duration": 0, - "primary_category": "" - } - }, - "bidder": { - "appnexus": { - "brand_id": 1, - "auction_id": 7311907164510136364, - "bidder_id": 2, - "bid_ad_type": 0 - } - } - } - }] - }], - "cur": "USD", - "ext": { - "responsetimemillis": { - "appnexus": 10 - }, - "tmaxrequest": 500 - } -} -``` - -### OpenRTB Extensions - -#### Conventions - -OpenRTB 2.5 permits exchanges to define their own extensions to any object from the spec. -These fall under the `ext` field of JSON objects. - -If `ext` is defined on an object, Prebid Server uses the following conventions: - -1. `ext` in "request objects" uses `ext.prebid` and/or `ext.{anyBidderCode}`. -2. `ext` on "response objects" uses `ext.prebid` and/or `ext.bidder`. -The only exception here is the top-level `BidResponse`, because it's bidder-independent. - -`ext.{anyBidderCode}` and `ext.bidder` extensions are defined by bidders. -`ext.prebid` extensions are defined by Prebid Server. - -Exceptions are made for extensions with "standard" recommendations: - -- `request.user.ext.digitrust` -- To support Digitrust -- `request.regs.ext.gdpr` and `request.user.ext.consent` -- To support GDPR -- `request.regs.us_privacy` -- To support CCPA -- `request.site.ext.amp` -- To identify AMP as the request source -- `request.app.ext.source` and `request.app.ext.version` -- To support identifying the displaymanager/SDK in mobile apps. If given, we expect these to be strings. - -#### Bid Adjustments - -Bidders [are encouraged](../../developers/add-new-bidder.md) to make Net bids. However, there's no way for Prebid to enforce this. -If you find that some bidders use Gross bids, publishers can adjust for it with `request.ext.prebid.bidadjustmentfactors`: - -``` -{ - "ext": { - "prebid": { - "bidadjustmentfactors": { - "appnexus": 0.8, - "rubicon": 0.7 - } - } - } -} -``` - -This may also be useful for publishers who want to account for different discrepancies with different bidders. - -#### Targeting - -Targeting refers to strings which are sent to the adserver to -[make header bidding possible](http://prebid.org/overview/intro.html#how-does-prebid-work). - -`request.ext.prebid.targeting` is an optional property which causes Prebid Server -to set these params on the response at `response.seatbid[i].bid[j].ext.prebid.targeting`. - -**Request format** (optional param `request.ext.prebid.targeting`) - -``` -{ - "ext": { - "prebid": { - "targeting": { - "pricegranularity": { - "precision": 2, - "ranges": [{ - "max": 20.00, - "increment": 0.10 // This is equivalent to the deprecated "pricegranularity": "medium" - }] - }, - "includewinners": false, // Optional param defaulting to true - "includebidderkeys": false // Optional param defaulting to true - "includeformat": false // Optional param defaulting to false - } - } - } -} -``` -The list of price granularity ranges must be given in order of increasing `max` values. If `precision` is omitted, it will default to `2`. The minimum of a range will be 0 or the previous `max`. Any cmp above the largest `max` will go in the `max` pricebucket. - -For backwards compatibility the following strings will also be allowed as price granularity definitions. There is no guarantee that these will be honored in the future. "One of ['low', 'med', 'high', 'auto', 'dense']" See [price granularity definitions](http://prebid.org/prebid-mobile/adops-price-granularity.html) - -One of "includewinners" or "includebidderkeys" must be true (both default to true if unset). If both were false, then no targeting keys would be set, which is better configured by omitting targeting altogether. - -The parameter "includeformat" indicates the type of the bid (banner, video, etc) for multiformat requests. It will add the key `hb_format` and/or `hb_format_{bidderName}` as per "includewinners" and "includebidderkeys" above. - -MediaType PriceGranularity (PBS-Java only) - when a single OpenRTB request contains multiple impressions with different mediatypes, or a single impression supports multiple formats, the different mediatypes may need different price granularities. If `mediatypepricegranularity` is present, `pricegranularity` would only be used for any mediatypes not specified. - -``` -{ - "ext": { - "prebid": { - "targeting": { - "mediatypepricegranularity": { - "banner": { - "ranges": [ - {"max": 20, "increment": 0.5} - ] - }, - "video": { - "ranges": [ - {"max": 10, "increment": 1}, - {"max": 20, "increment": 2}, - {"max": 50, "increment": 5} - ] - } - } - }, - "includewinners": true - } - } -} -``` - -**Response format** (returned in `bid.ext.prebid.targeting`) - -``` -{ - "seatbid": [{ - "bid": [{ - ... - "ext": { - "prebid": { - "targeting": { - "hb_bidder_{bidderName}": "The seatbid.seat which contains this bid", - "hb_size_{bidderName}": "A string like '300x250' using bid.w and bid.h for this bid", - "hb_pb_{bidderName}": "The bid.cpm, rounded down based on the price granularity." - } - } - } - }] - }] -} -``` - -The winning bid for each `request.imp[i]` will also contain `hb_bidder`, `hb_size`, and `hb_pb` -(with _no_ {bidderName} suffix). To prevent these keys, set `request.ext.prebid.targeting.includeWinners` to false. - -**NOTE**: Targeting keys are limited to 20 characters. If {bidderName} is too long, the returned key -will be truncated to only include the first 20 characters. - -#### Cookie syncs - -Each Bidder should receive their own ID in the `request.user.buyeruid` property. -Prebid Server has three ways to populate this field. In order of priority: - -1. If the request payload contains `request.user.buyeruid`, then that value will be sent to all Bidders. -In most cases, this is probably a bad idea. - -2. The request payload can store a `buyeruid` for each Bidder by defining `request.user.ext.prebid.buyeruids` like so: - -``` -{ - "user": { - "ext": { - "prebid": { - "buyeruids": { - "appnexus": "some-appnexus-id", - "rubicon": "some-rubicon-id" - } - } - } - } -} -``` - -Prebid Server's core logic will preprocess the request so that each Bidder sees their own value in the `request.user.buyeruid` field. - -3. Prebid Server will use its Cookie to map IDs for each Bidder. - -If you're using [Prebid.js](https://github.com/prebid/Prebid.js), this is happening automatically. - -If you're using another client, you can populate the Cookie of the Prebid Server host with User IDs -for each Bidder by using the `/cookie_sync` endpoint, and calling the URLs that it returns in the response. - -#### Native Request - -For each native request, the `assets` object's `id` field must not be defined. Prebid Server will set this automatically, using the index of the asset in the array as the ID. - - -#### Bidder Aliases - -Requests can define Bidder aliases if they want to refer to a Bidder by a separate name. -This can be used to request bids from the same Bidder with different params. For example: - -``` -{ - "imp": [{ - "id": "some-impression-id", - "video": { - "mimes": ["video/mp4"] - }, - "ext": { - "appnexus": { - "placementId": 123 - }, - "districtm": { - "placementId": 456 - } - } - }], - "ext": { - "prebid": { - "aliases": { - "districtm": "appnexus" - } - } - } -} -``` - -For all intents and purposes, the alias will be treated as another Bidder. This new Bidder will behave exactly -like the original, except that the Response will contain separate SeatBids, and any Targeting keys -will be formed using the alias' name. - -If an alias overlaps with a core Bidder's name, then the alias will take precedence. -This prevents breaking API changes as new Bidders are added to the project. - -For example, if the Request defines an alias like this: - -``` - "aliases": { - "appnexus": "rubicon" - } -``` - -then any `imp.ext.appnexus` params will actually go to the **rubicon** adapter. -It will become impossible to fetch bids from AppNexus within that Request. - -#### Bidder Response Times - -`response.ext.responsetimemillis.{bidderName}` tells how long each bidder took to respond. -These can help quantify the performance impact of "the slowest bidder." - -#### Bidder Errors - -`response.ext.errors.{bidderName}` contains messages which describe why a request may be "suboptimal". -For example, suppose a `banner` and a `video` impression are offered to a bidder -which only supports `banner`. - -In cases like these, the bidder can ignore the `video` impression and bid on the `banner` one. -However, the publisher can improve performance by only offering impressions which the bidder supports. - -For example, a request may return this in `response.ext` - -``` -{ - "ext": { - "errors": { - "appnexus": [{ - "code": 2, - "message": "A hybrid Banner/Audio Imp was offered, but Appnexus doesn't support Audio." - }], - "rubicon": [{ - "code": 1, - "message": "The request exceeded the timeout allocated" - }] - } - } -} -``` - -The codes currently defined are: - -``` -0 NoErrorCode -1 TimeoutCode -2 BadInputCode -3 BadServerResponseCode -999 UnknownErrorCode -``` - -#### Debugging - -`response.ext.debug.httpcalls.{bidder}` will be populated **only if** `request.test` **was set to 1**. - -This contains info about every request and response sent by the bidder to its server. -It is only returned on `test` bids for performance reasons, but may be useful during debugging. - -`response.ext.debug.resolvedrequest` will be populated **only if** `request.test` **was set to 1**. - -This contains the request after the resolution of stored requests and implicit information (e.g. site domain, device user agent). - -#### Stored Requests - -`request.imp[i].ext.prebid.storedrequest` incorporates a [Stored Request](../../developers/stored-requests.md) from the server. - -A typical `storedrequest` value looks like this: - -``` -{ - "imp": [{ - "ext": { - "prebid": { - "storedrequest": { - "id": "some-id" - } - } - } - }] -} -``` - -For more information, see the docs for [Stored Requests](../../developers/stored-requests.md). - -#### Cache bids - -Bids can be temporarily cached on the server by sending the following data as `request.ext.prebid.cache`: - -``` -{ - "ext": { - "prebid": { - "cache": { - "bids": {}, - "vastxml": {} - } - } - } -} -``` - -Both `bids` and `vastxml` are optional, but one of the two is required if you want to cache bids. This property will have no effect -unless `request.ext.prebid.targeting` is also set in the request. - -If `bids` is present, Prebid Server will make a _best effort_ to include these extra -`bid.ext.prebid.targeting` keys: - -- `hb_cache_id`: On the highest overall Bid in each Imp. -- `hb_cache_id_{bidderName}`: On the highest Bid from {bidderName} in each Imp. - -Clients _should not assume_ that these keys will exist, just because they were requested, though. -If they exist, the value will be a UUID which can be used to fetch Bid JSON from [Prebid Cache](https://github.com/prebid/prebid-cache). -They may not exist if the host company's cache is full, having connection problems, or other issues like that. - -If `vastxml` is present, PBS will try to add analogous keys `hb_uuid` and `hb_uuid_{bidderName}`. -In addition to the caveats above, these will exist _only if the relevant Bids are for Video_. -If they exist, the values can be used to fetch the bid's VAST XML from Prebid Cache directly. - -These options are mainly intended for certain limited Prebid Mobile setups, where bids cannot be cached client-side. - -#### GDPR - -Prebid Server supports the IAB's GDPR recommendations, which can be found [here](https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf). - -This adds two optional properties: - -- `request.user.ext.consent`: Is the consent string required by the IAB standards. -- `request.regs.ext.gdpr`: Is 0 if the caller believes that the user is *not* under GDPR, 1 if the user *is* under GDPR, and undefined if we're not certain. - -These fields will be forwarded to each Bidder, so they can decide how to process them. - -#### Interstitial support -Additional support for interstitials is enabled through the addition of two fields to the request: -device.ext.prebid.interstitial.minwidthperc and device.ext.interstial.minheightperc -The values will be numbers that indicate the minimum allowed size for the ad, as a percentage of the base side. For example, a width of 600 and "minwidthperc": 60 would allow ads with widths from 360 to 600 pixels inclusive. - -Example: -``` -{ - "imp": [{ - ... - "banner": { - ... - } - "instl": 1, - ... - }] - "device": { - ... - "h": 640, - "w": 320, - "ext": { - "prebid": { - "interstitial": { - "minwidthperc": 60, - "minheightperc": 60 - } - } - } - } -} -``` - -PBS receiving a request for an interstitial imp and these parameters set, it will rewrite the format object within the interstitial imp. If the format array's first object is a size, PBS will take it as the max size for the interstitial. If that size is 1x1, it will look up the device's size and use that as the max size. If the format is not present, it will also use the device size as the max size. (1x1 support so that you don't have to omit the format object to use the device size) -PBS with interstitial support will come preconfigured with a list of common ad sizes. Preferentially organized by weighing the larger and more common sizes first. But no guarantees to the ordering will be made. PBS will generate a new format list for the interstitial imp by traversing this list and picking the first 10 sizes that fall within the imp's max size and minimum percentage size. There will be no attempt to favor aspect ratios closer to the original size's aspect ratio. The limit of 10 is enforced to ensure we don't overload bidders with an overlong list. All the interstitial parameters will still be passed to the bidders, so they may recognize them and use their own size matching algorithms if they prefer. - -#### Currency Support - -To set the desired 'ad server currency', use the standard OpenRTB `cur` attribute. Note that Prebid Server only looks at the first currency in the array. - -``` - "cur": ["USD"] -``` - -If you want or need to define currency conversion rates (e.g. for currencies that your Prebid Server doesn't support), -define ext.prebid.currency.rates. (Currently supported in PBS-Java only) - -``` -"ext": { - "prebid": { - "currency": { - "rates": { - "USD": { "UAH": 24.47, "ETB": 32.04 } - } - } - } -} -``` - -If it exists, a rate defined in ext.prebid.currency.rates has the highest priority. -If a currency rate doesn't exist in the request, the external file will be used. - -#### Supply Chain Support - - -Basic supply chains are passed to Prebid Server on `source.ext.schain` and passed through to bid adapters. Prebid Server does not currently offer the ability to add a node to the supply chain. - -Bidder-specific schains (PBS-Java only): - -``` -ext.prebid.schains: [ - { bidders: ["bidderA"], schain: { SCHAIN OBJECT 1}}, - { bidders: ["*"], schain: { SCHAIN OBJECT 2}} -] -``` -In this scenario, Prebid Server sends the first schain object to `bidderA` and the second schain object to everyone else. - -If there's already an source.ext.schain and a bidder is named in ext.prebid.schains (or covered by the wildcard condition), ext.prebid.schains takes precedent. - -#### Rewarded Video (PBS-Java only) - -Rewarded video is a way to incentivize users to watch ads by giving them 'points' for viewing an ad. A Prebid Server -client can declare a given adunit as eligible for rewards by declaring `imp.ext.prebid.is_rewarded_inventory:1`. - -#### Stored Responses (PBS-Java only) - -While testing SDK and video integrations, it's important, but often difficult, to get consistent responses back from bidders that cover a range of scenarios like different CPM values, deals, etc. Prebid Server supports a debugging workflow in two ways: - -- a stored-auction-response that covers multiple bidder responses -- multiple stored-bid-responses at the bidder adapter level - -**Single Stored Auction Response ID** - -When a storedauctionresponse ID is specified: - -- the rest of the ext.prebid block is irrelevant and ignored -- nothing is sent to any bidder adapter for that imp -- the response retrieved from the stored-response-id is assumed to be the entire contents of the seatbid object corresponding to that impression. - -This request: -``` -{ - "test":1, - "tmax":500, - "id": "test-auction-id", - "app": { ... }, - "ext": { - "prebid": { - "targeting": {}, - "cache": { "bids": {} } - } - }, - "imp": [ - { - "id": "a", - "ext": { "prebid": { "storedauctionresponse": { "id": "1111111111" } } } - }, - { - "id": "b", - "ext": { "prebid": { "storedauctionresponse": { "id": "22222222222" } } } - } - ] -} -``` - -Will result in this response, assuming that the ids exist in the appropriate DB table read by Prebid Server: -``` -{ - "id": "test-auction-id", - "seatbid": [ - { - // BidderA bids from storedauctionresponse=1111111111 - // BidderA bids from storedauctionresponse=22222222 - }, - { - // BidderB bids from storedauctionresponse=1111111111 - // BidderB bids from storedauctionresponse=22222222 - } - ] -} -``` - -**Multiple Stored Bid Response IDs** - -In contrast to what's outlined above, this approach lets some real auctions take place while some bidders have test responses that still exercise bidder code. For example, this request: - -``` -{ - "test":1, - "tmax":500, - "id": "test-auction-id", - "app": { ... }, - "ext": { - "prebid": { - "targeting": {}, - "cache": { "bids": {} } - } - }, - "imp": [ - { - "id": "a", - "ext": { - "prebid": { - "storedbidresponse": [ - { "bidder": "BidderA", "id": "333333" }, - { "bidder": "BidderB", "id": "444444" }, - ] - } - } - }, - { - "id": "b", - "ext": { - "prebid": { - "storedbidresponse": [ - { "bidder": "BidderA", "id": "5555555" }, - { "bidder": "BidderB", "id": "6666666" }, - ] - } - } - } - ] -} -``` -Could result in this response: - -``` -{ - "id": "test-auction-id", - "seatbid": [ - { - "bid": [ - // contents of storedbidresponse=3333333 as parsed by bidderA adapter - // contents of storedbidresponse=5555555 as parsed by bidderA adapter - ] - }, - { - // contents of storedbidresponse=4444444 as parsed by bidderB adapter - // contents of storedbidresponse=6666666 as parsed by bidderB adapter - } - ] -} -``` - -Setting up the storedresponse DB entries is the responsibility of each Prebid Server host company. - -See Prebid.org troubleshooting pages for how to utilize this feature within the context of the browser. - - -#### User IDs (PBS-Java only) - -Prebid Server adapters can support the [Prebid.js User ID modules](http://prebid.org/dev-docs/modules/userId.html) by reading the following extensions and passing them through to their server endpoints: - -``` -{ - "user": { - "ext": { - "eids": [{ - "source": "adserver.org", - "uids": [{ - "id": "111111111111", - "ext": { - "rtiPartner": "TDID" - } - }] - }, - { - "source": "pubcommon", - "id":"11111111" - } - ], - "digitrust": { - "id": "11111111111", - "keyv": 4 - } - } - } -} -``` - -#### First Party Data Support (PBS-Java only) - -This is the Prebid Server version of the Prebid.js First Party Data feature. It's a standard way for the page (or app) to supply first party data and control which bidders have access to it. - -It specifies where in the OpenRTB request non-standard attributes should be passed. For example: - -``` -{ - "ext": { - "prebid": { - "data": { "bidders": [ "rubicon", "appnexus" ] } // these are the bidders allowed to see protected data - } - }, - "site": { - "keywords": "", - "search": "", - "ext": { - data: { GLOBAL CONTEXT DATA } // only seen by bidders named in ext.prebid.data.bidders[] - } - }, - "user": { - "keywords": "", - "gender": "", - "yob": 1999, - "geo": {}, - "ext": { - data: { GLOBAL USER DATA } // only seen by bidders named in ext.prebid.data.bidders[] - } - }, - "imp": [ - "ext": { - "context": { - "keywords": "", - "search": "", - "data": { ADUNIT SPECFIC CONTEXT DATA } // can be seen by all bidders - } - } - ] -``` - -Prebid Server enforces the data permissioning - -So before passing the values to the bidder adapters, core will: - -1. check for ext.prebid.data.bidders -1. if it exists, store it locally, but remove it from the OpenRTB before being sent to the adapters -1. As the OpenRTB request is being sent to each adapter: - 1. if ext.prebid.data.bidders exists in the original request, and this bidder is on the list then copy site.ext.data, app.ext.data, and user.ext.data to their bidder request -- otherwise don't copy those blocks - 1. copy other objects as normal - -Each adapter must be coded to read the values from these locations and pass it to their endpoints appropriately. - -### OpenRTB Ambiguities - -This section describes the ways in which Prebid Server **implements** OpenRTB spec ambiguous parts. - -- `request.cur`: If `request.cur` is not specified in the bid request, Prebid Server will consider it as being `USD` whereas OpenRTB spec doesn't mention any default currency for bid request. -```request.cur: ['USD'] // Default value if not set``` - - -### OpenRTB Differences - -This section describes the ways in which Prebid Server **breaks** the OpenRTB spec. - -#### Allowed Bidders - -Prebid Server returns a 400 on requests which define `wseat` or `bseat`. -We may add support for these in the future, if there's compelling need. - -Instead, an impression is only offered to a bidder if `bidrequest.imp[i].ext.{bidderName}` exists. - -This supports publishers who want to sell different impressions to different bidders. - -#### Deprecated Properties - -This endpoint returns a 400 if the request contains deprecated properties (e.g. `imp.wmin`, `imp.hmax`). - -The error message in the response should describe how to "fix" the request to make it legal. -If the message is unclear, please [log an issue](https://github.com/prebid/prebid-server/issues) -or [submit a pull request](https://github.com/prebid/prebid-server/pulls) to improve it. - -#### Determining Bid Security (http/https) - -In the OpenRTB spec, `request.imp[i].secure` says: - -> Flag to indicate if the impression requires secure HTTPS URL creative assets and markup, -> where 0 = non-secure, 1 = secure. If omitted, the secure state is unknown, but non-secure -> HTTP support can be assumed. - -In Prebid Server, an `https` request which does not define `secure` will be forwarded to Bidders with a `1`. -Publishers who run `https` sites and want insecure ads can still set this to `0` explicitly. - -### See also - -- [The OpenRTB 2.5 spec](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf) diff --git a/docs/endpoints/setuid.md b/docs/endpoints/setuid.md deleted file mode 100644 index c1746806371..00000000000 --- a/docs/endpoints/setuid.md +++ /dev/null @@ -1,26 +0,0 @@ -# Saving User Syncs - -This endpoint is used during cookie syncs. For technical details, see the -[Cookie Sync developer docs](../developers/cookie-syncs.md). - -## `GET /setuid` - -This endpoint saves a UserID for a Bidder in the Cookie. Saved IDs will be recognized for 7 days before being considered "stale" and being re-synced. - -### Query Params - -- `bidder`: The FamilyName of the [Usersyncer](../../usersync/usersync.go) which is being synced. -- `uid`: The ID which the Bidder uses to recognize this user. If undefined, the UID for `bidder` will be deleted. -- `gdpr`: This should be `1` if GDPR is in effect, `0` if not, and undefined if the caller isn't sure -- `gdpr_consent`: This is required if `gdpr` is one, and optional (but encouraged) otherwise. If present, it should be an [unpadded base64-URL](https://tools.ietf.org/html/rfc4648#page-7) encoded [Vendor Consent String](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/Consent%20string%20and%20vendor%20list%20formats%20v1.1%20Final.md#vendor-consent-string-format-). - -If the `gdpr` and `gdpr_consent` params are included, this endpoint will _not_ write a cookie unless: - -1. The Vendor ID set by the Prebid Server host company has permission to save cookies for that user. -2. The Prebid Server host company did not configure it to run with GDPR support. - -If in doubt, contact the company hosting Prebid Server and ask if they're GDPR-ready. - -### Sample request - -`GET http://prebid.site.com/setuid?bidder=adnxs&uid=12345&gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw` diff --git a/docs/endpoints/status.md b/docs/endpoints/status.md deleted file mode 100644 index 0c252397423..00000000000 --- a/docs/endpoints/status.md +++ /dev/null @@ -1,9 +0,0 @@ -## `GET /status` - -This endpoint will return a 2xx response whenever Prebid Server is ready to serve requests. -Its exact response can be [configured](../developers/configuration.md) with the `status_response` -config option. For example, in `pbs.yaml`: - -```yaml -status_response: "ok" -``` From ceaf883f3d94332ef8678e8e1694e48dbce56020 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Tue, 25 Aug 2020 15:05:13 -0700 Subject: [PATCH 178/318] Fix bid dedup (#1456) Co-authored-by: Veronika Solovei --- exchange/exchange.go | 2 +- exchange/exchange_test.go | 72 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/exchange/exchange.go b/exchange/exchange.go index e465a78389b..1fbdfe8ea67 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -660,7 +660,7 @@ func applyCategoryMapping(ctx context.Context, requestExt *openrtb_ext.ExtReques // An older bid from a different seatBid we've already finished with oldSeatBid := (seatBids)[dupe.bidderName] if len(oldSeatBid.bids) == 1 { - seatBidsToRemove = append(seatBidsToRemove, bidderName) + seatBidsToRemove = append(seatBidsToRemove, dupe.bidderName) rejections = updateRejections(rejections, dupe.bidID, "Bid was deduplicated") } else { oldSeatBid.bids = append(oldSeatBid.bids[:dupe.bidIndex], oldSeatBid.bids[dupe.bidIndex+1:]...) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index a6f69f70c59..d1531237688 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1619,6 +1619,78 @@ func TestBidRejectionErrors(t *testing.T) { } } +func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) { + + categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") + if error != nil { + t.Errorf("Failed to create a category Fetcher: %v", error) + } + + requestExt := newExtRequestTranslateCategories(nil) + + targData := &targetData{ + priceGranularity: requestExt.Prebid.Targeting.PriceGranularity, + includeWinners: true, + } + + requestExt.Prebid.Targeting.DurationRangeSec = []int{30} + requestExt.Prebid.Targeting.IncludeBrandCategory.WithCategory = false + + cats1 := []string{"IAB1-3"} + cats2 := []string{"IAB1-4"} + + bidApn1 := openrtb.Bid{ID: "bid_idApn1", ImpID: "imp_idApn1", Price: 10.0000, Cat: cats1, W: 1, H: 1} + bidApn2 := openrtb.Bid{ID: "bid_idApn2", ImpID: "imp_idApn2", Price: 10.0000, Cat: cats2, W: 1, H: 1} + + bid1_Apn1 := pbsOrtbBid{&bidApn1, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + bid1_Apn2 := pbsOrtbBid{&bidApn2, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, 0} + + innerBidsApn1 := []*pbsOrtbBid{ + &bid1_Apn1, + } + + innerBidsApn2 := []*pbsOrtbBid{ + &bid1_Apn2, + } + + for i := 1; i < 10; i++ { + adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid) + + seatBidApn1 := pbsOrtbSeatBid{innerBidsApn1, "USD", nil, nil} + bidderNameApn1 := openrtb_ext.BidderName("appnexus1") + + seatBidApn2 := pbsOrtbSeatBid{innerBidsApn2, "USD", nil, nil} + bidderNameApn2 := openrtb_ext.BidderName("appnexus2") + + adapterBids[bidderNameApn1] = &seatBidApn1 + adapterBids[bidderNameApn2] = &seatBidApn2 + + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, &requestExt, adapterBids, categoriesFetcher, targData) + + assert.NoError(t, err, "Category mapping error should be empty") + assert.Len(t, rejections, 1, "There should be 1 bid rejection message") + assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_idApn(1|2)\] reason: Bid was deduplicated`), rejections[0], "Rejection message did not match expected") + assert.Len(t, bidCategory, 1, "Bidders category mapping should have only one element") + + var resultBid string + for bidId := range bidCategory { + resultBid = bidId + } + + if resultBid == "bid_idApn1" { + assert.Nil(t, seatBidApn2.bids, "Appnexus_2 seat bid should not have any bids back") + assert.Len(t, seatBidApn1.bids, 1, "Appnexus_1 seat bid should have only one back") + + } else { + assert.Nil(t, seatBidApn1.bids, "Appnexus_1 seat bid should not have any bids back") + assert.Len(t, seatBidApn2.bids, 1, "Appnexus_2 seat bid should have only one back") + + } + + } + +} + func TestUpdateRejections(t *testing.T) { rejections := []string{} From 1c9b521f0f98086b6f27c00762353d78977bc54f Mon Sep 17 00:00:00 2001 From: Daniel Cassidy Date: Thu, 27 Aug 2020 15:36:30 +0100 Subject: [PATCH 179/318] consumable: Correct width and height reported in response. (#1459) Prebid Server now responds with the width and height specified in the Bid Response from Consumable. Previously it would reuse the width and height specified in the Bid Request. That older behaviour was ported from an older version of the prebid.js adapter but is no longer valid. --- adapters/consumable/consumable.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go index 243f1b8000b..ff7451f15f7 100644 --- a/adapters/consumable/consumable.go +++ b/adapters/consumable/consumable.go @@ -69,6 +69,8 @@ type decision struct { CreativeID string `json:"creativeId,omitempty"` Contents []contents `json:"contents"` ImpressionUrl *string `json:"impressionUrl,omitempty"` + Width uint64 `json:"width,omitempty"` // Consumable extension, not defined by Adzerk + Height uint64 `json:"height,omitempty"` // Consumable extension, not defined by Adzerk } type contents struct { @@ -241,23 +243,13 @@ func (a *ConsumableAdapter) MakeBids( for impID, decision := range serverResponse.Decisions { if decision.Pricing != nil && decision.Pricing.ClearPrice != nil { - - imp := getImp(impID, internalRequest.Imp) - if imp == nil { - errors = append(errors, &errortypes.BadServerResponse{ - Message: fmt.Sprintf( - "ignoring bid id=%s, request doesn't contain any impression with id=%s", internalRequest.ID, impID), - }) - continue - } - bid := openrtb.Bid{} bid.ID = internalRequest.ID bid.ImpID = impID bid.Price = *decision.Pricing.ClearPrice bid.AdM = retrieveAd(decision) - bid.W = imp.Banner.Format[0].W // TODO: Review to check if this is correct behaviour - bid.H = imp.Banner.Format[0].H + bid.W = decision.Width + bid.H = decision.Height bid.CrID = strconv.FormatInt(decision.AdID, 10) bid.Exp = 30 // TODO: Check this is intention of TTL @@ -279,15 +271,6 @@ func (a *ConsumableAdapter) MakeBids( return bidderResponse, errors } -func getImp(impId string, imps []openrtb.Imp) *openrtb.Imp { - for _, imp := range imps { - if imp.ID == impId { - return &imp - } - } - return nil -} - func extractExtensions(impression openrtb.Imp) (*adapters.ExtImpBidder, *openrtb_ext.ExtImpConsumable, []error) { var bidderExt adapters.ExtImpBidder if err := json.Unmarshal(impression.Ext, &bidderExt); err != nil { From 1f8749789d261d23858db3724d01aaad6417c503 Mon Sep 17 00:00:00 2001 From: guscarreon Date: Thu, 27 Aug 2020 13:35:37 -0400 Subject: [PATCH 180/318] Panics happen when left with zero length []Imp (#1462) --- adapters/info.go | 8 +++ adapters/info_test.go | 142 +++++++++++++++++++++++++++++++----------- 2 files changed, 114 insertions(+), 36 deletions(-) diff --git a/adapters/info.go b/adapters/info.go index 732ae85589b..982827c90fb 100644 --- a/adapters/info.go +++ b/adapters/info.go @@ -37,6 +37,7 @@ type InfoAwareBidder struct { func (i *InfoAwareBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *ExtraRequestInfo) ([]*RequestData, []error) { var allowedMediaTypes parsedSupports + if request.Site != nil { if !i.info.site.enabled { return nil, []error{BadInput("this bidder does not support site requests")} @@ -56,7 +57,14 @@ func (i *InfoAwareBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *Ext // To avoid allocating new arrays and copying in the normal case, we'll make one pass to // see if any imps need to be removed, and another to do the removing if necessary. numToFilter, errs := i.pruneImps(request.Imp, allowedMediaTypes) + + // If all imps in bid request come with unsupported media types, exit + if numToFilter == len(request.Imp) { + return nil, append(errs, BadInput("Bid request didn't contain media types supported by the bidder")) + } + if numToFilter != 0 { + // Filter out imps with unsupported media types filteredImps, newErrs := i.filterImps(request.Imp, numToFilter) request.Imp = filteredImps errs = append(errs, newErrs...) diff --git a/adapters/info_test.go b/adapters/info_test.go index 9c0dd16babb..ce0f88696db 100644 --- a/adapters/info_test.go +++ b/adapters/info_test.go @@ -24,6 +24,7 @@ func TestAppNotSupported(t *testing.T) { } constrained := adapters.EnforceBidderInfo(bidder, info) bids, errs := constrained.MakeRequests(&openrtb.BidRequest{ + Imp: []openrtb.Imp{{ID: "imp-1", Banner: &openrtb.Banner{}}}, App: &openrtb.App{}, }, &adapters.ExtraRequestInfo{}) if !assert.Len(t, errs, 1) { @@ -45,6 +46,7 @@ func TestSiteNotSupported(t *testing.T) { } constrained := adapters.EnforceBidderInfo(bidder, info) bids, errs := constrained.MakeRequests(&openrtb.BidRequest{ + Imp: []openrtb.Imp{{ID: "imp-1", Banner: &openrtb.Banner{}}}, Site: &openrtb.Site{}, }, &adapters.ExtraRequestInfo{}) if !assert.Len(t, errs, 1) { @@ -69,48 +71,111 @@ func TestImpFiltering(t *testing.T) { } constrained := adapters.EnforceBidderInfo(bidder, info) - _, errs := constrained.MakeRequests(&openrtb.BidRequest{ - Imp: []openrtb.Imp{ - { - ID: "imp-1", - Video: &openrtb.Video{}, + + testCases := []struct { + description string + inBidRequest *openrtb.BidRequest + expectedErrors []error + expectedImpLen int + }{ + { + description: "Empty Imp array. MakeRequest() call not expected", + inBidRequest: &openrtb.BidRequest{ + Imp: []openrtb.Imp{}, + Site: &openrtb.Site{}, }, - { - Native: &openrtb.Native{}, + expectedErrors: []error{ + &errortypes.BadInput{Message: "Bid request didn't contain media types supported by the bidder"}, }, - { - ID: "imp-2", - Video: &openrtb.Video{}, - Native: &openrtb.Native{}, + expectedImpLen: 0, + }, + { + description: "Sole imp in bid request is of wrong media type. MakeRequest() call not expected", + inBidRequest: &openrtb.BidRequest{ + Imp: []openrtb.Imp{{ID: "imp-1", Video: &openrtb.Video{}}}, + App: &openrtb.App{}, }, - { - Banner: &openrtb.Banner{}, + expectedErrors: []error{ + &errortypes.BadInput{Message: "request.imp[0] uses video, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "Bid request didn't contain media types supported by the bidder"}, }, + expectedImpLen: 0, + }, + { + description: "All imps in bid request of wrong media type, MakeRequest() call not expected", + inBidRequest: &openrtb.BidRequest{ + Imp: []openrtb.Imp{ + {ID: "imp-1", Video: &openrtb.Video{}}, + {ID: "imp-2", Native: &openrtb.Native{}}, + {ID: "imp-3", Audio: &openrtb.Audio{}}, + }, + App: &openrtb.App{}, + }, + expectedErrors: []error{ + &errortypes.BadInput{Message: "request.imp[0] uses video, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "request.imp[1] uses native, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "request.imp[2] uses audio, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "Bid request didn't contain media types supported by the bidder"}, + }, + expectedImpLen: 0, + }, + { + description: "Some imps with correct media type, MakeRequest() call expected", + inBidRequest: &openrtb.BidRequest{ + Imp: []openrtb.Imp{ + { + ID: "imp-1", + Video: &openrtb.Video{}, + }, + { + Native: &openrtb.Native{}, + }, + { + ID: "imp-2", + Video: &openrtb.Video{}, + Native: &openrtb.Native{}, + }, + { + Banner: &openrtb.Banner{}, + }, + }, + Site: &openrtb.Site{}, + }, + expectedErrors: []error{ + &errortypes.BadInput{Message: "request.imp[1] uses native, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "request.imp[2] uses native, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "request.imp[3] uses banner, but this bidder doesn't support it"}, + &errortypes.BadInput{Message: "request.imp[1] has no supported MediaTypes. It will be ignored"}, + &errortypes.BadInput{Message: "request.imp[3] has no supported MediaTypes. It will be ignored"}, + }, + expectedImpLen: 2, + }, + { + description: "All imps with correct media type, MakeRequest() call expected", + inBidRequest: &openrtb.BidRequest{ + Imp: []openrtb.Imp{ + {ID: "imp-1", Video: &openrtb.Video{}}, + {ID: "imp-2", Video: &openrtb.Video{}}, + }, + Site: &openrtb.Site{}, + }, + expectedErrors: nil, + expectedImpLen: 2, }, - Site: &openrtb.Site{}, - }, &adapters.ExtraRequestInfo{}) - if !assert.Len(t, errs, 6) { - return } - assert.EqualError(t, errs[0], "request.imp[1] uses native, but this bidder doesn't support it") - assert.EqualError(t, errs[1], "request.imp[2] uses native, but this bidder doesn't support it") - assert.EqualError(t, errs[2], "request.imp[3] uses banner, but this bidder doesn't support it") - assert.EqualError(t, errs[3], "request.imp[1] has no supported MediaTypes. It will be ignored") - assert.EqualError(t, errs[4], "request.imp[3] has no supported MediaTypes. It will be ignored") - assert.EqualError(t, errs[5], "mock MakeRequests error") - assert.IsType(t, &errortypes.BadInput{}, errs[0]) - assert.IsType(t, &errortypes.BadInput{}, errs[1]) - assert.IsType(t, &errortypes.BadInput{}, errs[2]) - assert.IsType(t, &errortypes.BadInput{}, errs[3]) - assert.IsType(t, &errortypes.BadInput{}, errs[4]) - req := bidder.gotRequest - if !assert.Len(t, req.Imp, 2) { - return + for _, test := range testCases { + actualAdapterRequests, actualErrs := constrained.MakeRequests(test.inBidRequest, &adapters.ExtraRequestInfo{}) + + // Assert the request.Imp slice was correctly filtered and if MakeRequest() was called by asserting + // the corresponding error messages were returned + for i, expectedErr := range test.expectedErrors { + assert.EqualError(t, expectedErr, actualErrs[i].Error(), "Test failed. Error[%d] in error list mismatch: %s", i, test.description) + } + + // Extra MakeRequests() call check: our mockBidder returns an adapter request for every imp + assert.Len(t, actualAdapterRequests, test.expectedImpLen, "Test failed. Incorrect lenght of filtered imps: %s", test.description) } - assert.Equal(t, "imp-1", req.Imp[0].ID) - assert.Equal(t, "imp-2", req.Imp[1].ID) - assert.Nil(t, req.Imp[1].Native) } type mockBidder struct { @@ -118,8 +183,13 @@ type mockBidder struct { } func (m *mockBidder) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - m.gotRequest = request - return nil, []error{errors.New("mock MakeRequests error")} + var adapterRequests []*adapters.RequestData + + for i := 0; i < len(request.Imp); i++ { + adapterRequests = append(adapterRequests, &adapters.RequestData{}) + } + + return adapterRequests, nil } func (m *mockBidder) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { From 292df1f3f5da623c6c140542510da026b1c3cbd2 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Thu, 27 Aug 2020 14:19:25 -0400 Subject: [PATCH 181/318] Add Scheme Option To External Cache URL (#1460) --- config/config.go | 14 +- config/config_test.go | 17 +++ endpoints/openrtb2/video_auction_test.go | 4 +- exchange/auction_test.go | 17 ++- exchange/exchange.go | 61 +++++--- exchange/exchange_test.go | 141 +++++++++++++++++- .../exchangetest/targeting-cache-vast.json | 2 +- .../exchangetest/targeting-cache-zero.json | 2 +- prebid_cache_client/client.go | 30 ++-- prebid_cache_client/client_test.go | 74 +++++---- 10 files changed, 284 insertions(+), 78 deletions(-) diff --git a/config/config.go b/config/config.go index e3b7d8ebda0..c2e7dfd8f3f 100755 --- a/config/config.go +++ b/config/config.go @@ -136,6 +136,10 @@ func (data *ExternalCache) validate(errs configErrors) configErrors { return errs } + if data.Scheme != "" && data.Scheme != "http" && data.Scheme != "https" { + return append(errs, errors.New("External cache Scheme must be http or https if specified")) + } + // Either host or path or both not empty, validate. if data.Host == "" && data.Path != "" || data.Host != "" && data.Path == "" { return append(errs, errors.New("External cache Host and Path must both be specified")) @@ -486,13 +490,14 @@ type DataCache struct { TTLSeconds int `mapstructure:"ttl_seconds"` } -// Data type where we store the external cache URL elements. This is completely unrelated to type Cache struct defined afterwards, because -// the latter is used for internal cache URL while the following contains information of the external cache URL. +// ExternalCache configures the externally accessible cache url. type ExternalCache struct { - Host string `mapstructure:"host"` - Path string `mapstructure:"path"` + Scheme string `mapstructure:"scheme"` + Host string `mapstructure:"host"` + Path string `mapstructure:"path"` } +// Cache configures the url used internally by Prebid Server to communicate with Prebid Cache. type Cache struct { Scheme string `mapstructure:"scheme"` Host string `mapstructure:"host"` @@ -734,6 +739,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("cache.default_ttl_seconds.video", 0) v.SetDefault("cache.default_ttl_seconds.native", 0) v.SetDefault("cache.default_ttl_seconds.audio", 0) + v.SetDefault("external_cache.scheme", "") v.SetDefault("external_cache.host", "") v.SetDefault("external_cache.path", "") v.SetDefault("recaptcha_secret", "") diff --git a/config/config_test.go b/config/config_test.go index 3da3f72137b..40589ac7b23 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -90,6 +90,21 @@ func TestExternalCacheURLValidate(t *testing.T) { data: ExternalCache{Host: "http://", Path: ""}, expErrors: 1, }, + { + desc: "Scheme Invalid", + data: ExternalCache{Scheme: "invalid", Host: "www.google.com", Path: "/path/v1"}, + expErrors: 1, + }, + { + desc: "Scheme HTTP", + data: ExternalCache{Scheme: "http", Host: "www.google.com", Path: "/path/v1"}, + expErrors: 0, + }, + { + desc: "Scheme HTTPS", + data: ExternalCache{Scheme: "https", Host: "www.google.com", Path: "/path/v1"}, + expErrors: 0, + }, } for _, test := range testCases { var errs configErrors @@ -150,6 +165,7 @@ cache: host: prebidcache.net query: uuid=%PBS_CACHE_UUID% external_cache: + scheme: https host: www.externalprebidcache.net path: /endpoints/cache http_client: @@ -307,6 +323,7 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "cache.scheme", cfg.CacheURL.Scheme, "http") cmpStrings(t, "cache.host", cfg.CacheURL.Host, "prebidcache.net") cmpStrings(t, "cache.query", cfg.CacheURL.Query, "uuid=%PBS_CACHE_UUID%") + cmpStrings(t, "external_cache.scheme", cfg.ExtCacheURL.Scheme, "https") cmpStrings(t, "external_cache.host", cfg.ExtCacheURL.Host, "www.externalprebidcache.net") cmpStrings(t, "external_cache.path", cfg.ExtCacheURL.Path, "/endpoints/cache") cmpInts(t, "http_client.max_connections_per_host", cfg.Client.MaxConnsPerHost, 10) diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 534db3c79e2..78715f5c87d 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -1259,8 +1259,8 @@ func (m *mockCacheClient) PutJson(ctx context.Context, values []prebid_cache_cli return []string{}, []error{} } -func (m *mockCacheClient) GetExtCacheData() (string, string) { - return "", "" +func (m *mockCacheClient) GetExtCacheData() (scheme string, host string, path string) { + return "", "", "" } type mockVideoStoredReqFetcher struct { diff --git a/exchange/auction_test.go b/exchange/auction_test.go index e23c45cb494..9f24682bfc3 100644 --- a/exchange/auction_test.go +++ b/exchange/auction_test.go @@ -298,22 +298,27 @@ type pbsBid struct { Bidder openrtb_ext.BidderName `json:"bidder"` } -type mockCache struct { - items []prebid_cache_client.Cacheable -} - type cacheComparator struct { freq int expectedKeys []string actualKeys []string } -func (c *mockCache) GetExtCacheData() (string, string) { - return "", "" +type mockCache struct { + scheme string + host string + path string + items []prebid_cache_client.Cacheable +} + +func (c *mockCache) GetExtCacheData() (scheme string, host string, path string) { + return c.scheme, c.host, c.path } + func (c *mockCache) GetPutUrl() string { return "" } + func (c *mockCache) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) { c.items = values return []string{"", "", "", "", ""}, nil diff --git a/exchange/exchange.go b/exchange/exchange.go index 1fbdfe8ea67..59e876697cf 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -8,6 +8,7 @@ import ( "fmt" "math/rand" "net/http" + "net/url" "runtime/debug" "sort" "strconv" @@ -107,7 +108,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque shouldCacheBids, shouldCacheVAST := getExtCacheInfo(requestExt) targData := getExtTargetData(requestExt, shouldCacheBids, shouldCacheVAST) if targData != nil { - targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() + _, targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() } debugInfo := getDebugInfo(bidRequest, requestExt) @@ -814,24 +815,50 @@ func (e *exchange) makeBid(Bids []*pbsOrtbBid, adapter openrtb_ext.BidderName, a // If bid got cached inside `(a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string)`, // a UUID should be found inside `a.cacheIds` or `a.vastCacheIds`. This function returns the UUID along with the internal cache URL -func (e *exchange) getBidCacheInfo(bid *pbsOrtbBid, auc *auction) (openrtb_ext.ExtBidPrebidCacheBids, bool) { - var cacheInfo openrtb_ext.ExtBidPrebidCacheBids - var cacheUUID string - var found bool = false - - if auc != nil { - var extCacheHost, extCachePath string - if cacheUUID, found = auc.cacheIds[bid.bid]; found { - cacheInfo.CacheId = cacheUUID - extCacheHost, extCachePath = e.cache.GetExtCacheData() - cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID - } else if cacheUUID, found = auc.vastCacheIds[bid.bid]; found { - cacheInfo.CacheId = cacheUUID - extCacheHost, extCachePath = e.cache.GetExtCacheData() - cacheInfo.Url = extCacheHost + extCachePath + "?uuid=" + cacheUUID +func (e *exchange) getBidCacheInfo(bid *pbsOrtbBid, auction *auction) (cacheInfo openrtb_ext.ExtBidPrebidCacheBids, found bool) { + uuid, found := findCacheID(bid, auction) + + if found { + cacheInfo.CacheId = uuid + cacheInfo.Url = buildCacheURL(e.cache, uuid) + } + + return +} + +func findCacheID(bid *pbsOrtbBid, auction *auction) (string, bool) { + if bid != nil && bid.bid != nil && auction != nil { + if id, found := auction.cacheIds[bid.bid]; found { + return id, true + } + + if id, found := auction.vastCacheIds[bid.bid]; found { + return id, true } } - return cacheInfo, found + + return "", false +} + +func buildCacheURL(cache prebid_cache_client.Client, uuid string) string { + scheme, host, path := cache.GetExtCacheData() + + if host == "" || path == "" { + return "" + } + + query := url.Values{"uuid": []string{uuid}} + cacheURL := url.URL{ + Scheme: scheme, + Host: host, + Path: path, + RawQuery: query.Encode(), + } + cacheURL.Query() + + // URLs without a scheme will begin with //, in which case we + // want to trim it off to keep compatbile with current behavior. + return strings.TrimPrefix(cacheURL.String(), "//") } func listBiddersWithRequests(cleanRequests map[openrtb_ext.BidderName]*openrtb.BidRequest) []openrtb_ext.BidderName { diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index d1531237688..efabb845211 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -274,9 +274,10 @@ func TestDebugBehaviour(t *testing.T) { } } -func TestGetBidCacheInfo(t *testing.T) { +func TestGetBidCacheInfoEndToEnd(t *testing.T) { testUUID := "CACHE_UUID_1234" - testExternalCacheHost := "https://www.externalprebidcache.net" + testExternalCacheScheme := "https" + testExternalCacheHost := "www.externalprebidcache.net" testExternalCachePath := "endpoints/cache" /* 1) An adapter */ @@ -292,8 +293,9 @@ func TestGetBidCacheInfo(t *testing.T) { Host: "www.internalprebidcache.net", }, ExtCacheURL: config.ExternalCache{ - Host: testExternalCacheHost, - Path: testExternalCachePath, + Scheme: testExternalCacheScheme, + Host: testExternalCacheHost, + Path: testExternalCachePath, }, } adapterList := make([]openrtb_ext.BidderName, 0, 2) @@ -421,7 +423,7 @@ func TestGetBidCacheInfo(t *testing.T) { Seat: string(bidderName), Bid: []openrtb.Bid{ { - Ext: json.RawMessage(`{ "prebid": { "cache": { "bids": { "cacheId": "` + testUUID + `", "url": "` + testExternalCacheHost + `/` + testExternalCachePath + `?uuid=` + testUUID + `" }, "key": "", "url": "" }`), + Ext: json.RawMessage(`{ "prebid": { "cache": { "bids": { "cacheId": "` + testUUID + `", "url": "` + testExternalCacheScheme + `://` + testExternalCacheHost + `/` + testExternalCachePath + `?uuid=` + testUUID + `" }, "key": "", "url": "" }`), }, }, }, @@ -446,6 +448,131 @@ func TestGetBidCacheInfo(t *testing.T) { assert.Equal(t, expCacheURL, cacheURL, "[TestGetBidCacheInfo] cacheId field in ext should equal \"%s\" \n", expCacheURL) } +func TestGetBidCacheInfo(t *testing.T) { + bid := &openrtb.Bid{ID: "42"} + testCases := []struct { + description string + scheme string + host string + path string + bid *pbsOrtbBid + auction *auction + expectedFound bool + expectedCacheID string + expectedCacheURL string + }{ + { + description: "JSON Cache ID", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: true, + expectedCacheID: "anyID", + expectedCacheURL: "https://prebid.org/cache?uuid=anyID", + }, + { + description: "VAST Cache ID", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{vastCacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: true, + expectedCacheID: "anyID", + expectedCacheURL: "https://prebid.org/cache?uuid=anyID", + }, + { + description: "Cache ID Not Found", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{}, + expectedFound: false, + expectedCacheID: "", + expectedCacheURL: "", + }, + { + description: "Scheme Not Provided", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: true, + expectedCacheID: "anyID", + expectedCacheURL: "prebid.org/cache?uuid=anyID", + }, + { + description: "Host And Path Not Provided - Without Scheme", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: true, + expectedCacheID: "anyID", + expectedCacheURL: "", + }, + { + description: "Host And Path Not Provided - With Scheme", + scheme: "https", + bid: &pbsOrtbBid{bid: bid}, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: true, + expectedCacheID: "anyID", + expectedCacheURL: "", + }, + { + description: "Nil Bid", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: nil, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: false, + expectedCacheID: "", + expectedCacheURL: "", + }, + { + description: "Nil Embedded Bid", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: nil}, + auction: &auction{cacheIds: map[*openrtb.Bid]string{bid: "anyID"}}, + expectedFound: false, + expectedCacheID: "", + expectedCacheURL: "", + }, + { + description: "Nil Auction", + scheme: "https", + host: "prebid.org", + path: "cache", + bid: &pbsOrtbBid{bid: bid}, + auction: nil, + expectedFound: false, + expectedCacheID: "", + expectedCacheURL: "", + }, + } + + for _, test := range testCases { + exchange := &exchange{ + cache: &mockCache{ + scheme: test.scheme, + host: test.host, + path: test.path, + }, + } + + cacheInfo, found := exchange.getBidCacheInfo(test.bid, test.auction) + + assert.Equal(t, test.expectedFound, found, test.description+":found") + assert.Equal(t, test.expectedCacheID, cacheInfo.CacheId, test.description+":id") + assert.Equal(t, test.expectedCacheURL, cacheInfo.Url, test.description+":url") + } +} + func TestBidResponseCurrency(t *testing.T) { // Init objects cfg := &config.Configuration{Adapters: make(map[string]config.Adapter, 1)} @@ -2176,8 +2303,8 @@ func mockSlowHandler(delay time.Duration, statusCode int, body string) http.Hand type wellBehavedCache struct{} -func (c *wellBehavedCache) GetExtCacheData() (string, string) { - return "www.pbcserver.com", "/pbcache/endpoint" +func (c *wellBehavedCache) GetExtCacheData() (scheme string, host string, path string) { + return "https", "www.pbcserver.com", "/pbcache/endpoint" } func (c *wellBehavedCache) PutJson(ctx context.Context, values []pbc.Cacheable) ([]string, []error) { diff --git a/exchange/exchangetest/targeting-cache-vast.json b/exchange/exchangetest/targeting-cache-vast.json index f348dd1b29d..53a48c4ec69 100644 --- a/exchange/exchangetest/targeting-cache-vast.json +++ b/exchange/exchangetest/targeting-cache-vast.json @@ -67,7 +67,7 @@ "cache": { "bids": { "cacheId": "0", - "url": "www.pbcserver.com/pbcache/endpoint?uuid=0" + "url": "https://www.pbcserver.com/pbcache/endpoint?uuid=0" }, "key": "", "url": "" diff --git a/exchange/exchangetest/targeting-cache-zero.json b/exchange/exchangetest/targeting-cache-zero.json index 5130153026a..0048ea10917 100644 --- a/exchange/exchangetest/targeting-cache-zero.json +++ b/exchange/exchangetest/targeting-cache-zero.json @@ -70,7 +70,7 @@ "cache": { "bids": { "cacheId": "0", - "url": "www.pbcserver.com/pbcache/endpoint?uuid=0" + "url": "https://www.pbcserver.com/pbcache/endpoint?uuid=0" }, "key": "", "url": "" diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go index a5730ce7914..1ad788300a9 100644 --- a/prebid_cache_client/client.go +++ b/prebid_cache_client/client.go @@ -29,8 +29,8 @@ type Client interface { // logging any relevant errors to the app logs PutJson(ctx context.Context, values []Cacheable) ([]string, []error) - // Serves the purpose of a getter that returns the host and the cache of the prebid-server URL - GetExtCacheData() (string, string) + // GetExtCacheData gets the scheme, host, and path of the externally accessible cache url. + GetExtCacheData() (scheme string, host string, path string) } type PayloadType string @@ -49,23 +49,25 @@ type Cacheable struct { func NewClient(httpClient *http.Client, conf *config.Cache, extCache *config.ExternalCache, metrics pbsmetrics.MetricsEngine) Client { return &clientImpl{ - httpClient: httpClient, - putUrl: conf.GetBaseURL() + "/cache", - externalCacheHost: extCache.Host, - externalCachePath: extCache.Path, - metrics: metrics, + httpClient: httpClient, + putUrl: conf.GetBaseURL() + "/cache", + externalCacheScheme: extCache.Scheme, + externalCacheHost: extCache.Host, + externalCachePath: extCache.Path, + metrics: metrics, } } type clientImpl struct { - httpClient *http.Client - putUrl string - externalCacheHost string - externalCachePath string - metrics pbsmetrics.MetricsEngine + httpClient *http.Client + putUrl string + externalCacheScheme string + externalCacheHost string + externalCachePath string + metrics pbsmetrics.MetricsEngine } -func (c *clientImpl) GetExtCacheData() (string, string) { +func (c *clientImpl) GetExtCacheData() (string, string, string) { path := c.externalCachePath if path == "/" { // Only the slash for the path, remove it to empty @@ -75,7 +77,7 @@ func (c *clientImpl) GetExtCacheData() (string, string) { path = "/" + path } - return c.externalCacheHost, path + return c.externalCacheScheme, c.externalCacheHost, path } func (c *clientImpl) PutJson(ctx context.Context, values []Cacheable) (uuids []string, errs []error) { diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go index 5840d4ea564..72fd2761731 100644 --- a/prebid_cache_client/client_test.go +++ b/prebid_cache_client/client_test.go @@ -186,58 +186,80 @@ func TestEncodeValueToBuffer(t *testing.T) { func TestStripCacheHostAndPath(t *testing.T) { inCacheURL := config.Cache{ExpectedTimeMillis: 10} type aTest struct { - inExtCacheURL config.ExternalCache - expectedHost string - expectedPath string + inExtCacheURL config.ExternalCache + expectedScheme string + expectedHost string + expectedPath string } testInput := []aTest{ { inExtCacheURL: config.ExternalCache{ - Host: "prebid-server.prebid.org", - Path: "/pbcache/endpoint", + Scheme: "", + Host: "prebid-server.prebid.org", + Path: "/pbcache/endpoint", }, - expectedHost: "prebid-server.prebid.org", - expectedPath: "/pbcache/endpoint", + expectedScheme: "", + expectedHost: "prebid-server.prebid.org", + expectedPath: "/pbcache/endpoint", }, { inExtCacheURL: config.ExternalCache{ - Host: "prebidcache.net", - Path: "", + Scheme: "https", + Host: "prebid-server.prebid.org", + Path: "/pbcache/endpoint", }, - expectedHost: "prebidcache.net", - expectedPath: "", + expectedScheme: "https", + expectedHost: "prebid-server.prebid.org", + expectedPath: "/pbcache/endpoint", }, { inExtCacheURL: config.ExternalCache{ - Host: "", - Path: "", + Scheme: "", + Host: "prebidcache.net", + Path: "", }, - expectedHost: "", - expectedPath: "", + expectedScheme: "", + expectedHost: "prebidcache.net", + expectedPath: "", }, { inExtCacheURL: config.ExternalCache{ - Host: "prebid-server.prebid.org", - Path: "pbcache/endpoint", + Scheme: "", + Host: "", + Path: "", }, - expectedHost: "prebid-server.prebid.org", - expectedPath: "/pbcache/endpoint", + expectedScheme: "", + expectedHost: "", + expectedPath: "", }, { inExtCacheURL: config.ExternalCache{ - Host: "prebidcache.net", - Path: "/", + Scheme: "", + Host: "prebid-server.prebid.org", + Path: "pbcache/endpoint", }, - expectedHost: "prebidcache.net", - expectedPath: "", + expectedScheme: "", + expectedHost: "prebid-server.prebid.org", + expectedPath: "/pbcache/endpoint", + }, + { + inExtCacheURL: config.ExternalCache{ + Scheme: "", + Host: "prebidcache.net", + Path: "/", + }, + expectedScheme: "", + expectedHost: "prebidcache.net", + expectedPath: "", }, } for _, test := range testInput { cacheClient := NewClient(&http.Client{}, &inCacheURL, &test.inExtCacheURL, &metricsConf.DummyMetricsEngine{}) - cHost, cPath := cacheClient.GetExtCacheData() + scheme, host, path := cacheClient.GetExtCacheData() - assert.Equal(t, test.expectedHost, cHost) - assert.Equal(t, test.expectedPath, cPath) + assert.Equal(t, test.expectedScheme, scheme) + assert.Equal(t, test.expectedHost, host) + assert.Equal(t, test.expectedPath, path) } } From 5d13c8595b343893c2652ad135207bf0f58263c5 Mon Sep 17 00:00:00 2001 From: GammaSSP <35954362+gammassp@users.noreply.github.com> Date: Fri, 28 Aug 2020 22:54:11 +0700 Subject: [PATCH 182/318] Update gamma adapter (#1447) * Gamma SSP Adapter * Add Gamma SSP server adapter * increase coverage * Fix conflict with base master * Add check MediaType for Imp * Implement Multi Imps request * Changes requested * remove bad-request * increase coverage * Remove duplicate test file * Update gamma.go * Update gamma.go * Update gamma.go * Update config.go Remove Gamma User Sync Url from config * Gamma SSP Adapter * Add Gamma SSP server adapter * increase coverage * Fix conflict with base master * Add check MediaType for Imp * Implement Multi Imps request * Changes requested * remove bad-request * increase coverage * Remove duplicate test file * Update gamma.go * Update gamma.go * update gamma adapter * return nil when have No-Bid Signaling * add missing-adm.json * discard the bid that's missing adm * discard the bid that's missing adm * escape vast instead of encoded it * expand test coverage Co-authored-by: Easy Life --- adapters/gamma/gamma.go | 77 ++++++++++++++++--- .../exemplary/banner-and-video-and-audio.json | 15 ++-- .../exemplary/valid-full-params.json | 2 +- .../gammatest/supplemental/bad-request.json | 2 +- .../gammatest/supplemental/missing-adm.json | 76 ++++++++++++++++++ .../gammatest/supplemental/missing-param.json | 2 +- .../gammatest/supplemental/missing-zone.json | 2 +- .../supplemental/nobid-signaling.json | 56 ++++++++++++++ .../supplemental/status-forbidden.json | 2 +- .../supplemental/status-no-content.json | 2 +- 10 files changed, 215 insertions(+), 21 deletions(-) create mode 100644 adapters/gamma/gammatest/supplemental/missing-adm.json create mode 100644 adapters/gamma/gammatest/supplemental/nobid-signaling.json diff --git a/adapters/gamma/gamma.go b/adapters/gamma/gamma.go index c011954fff9..3756723598f 100644 --- a/adapters/gamma/gamma.go +++ b/adapters/gamma/gamma.go @@ -17,6 +17,27 @@ type GammaAdapter struct { URI string } +type gammaBid struct { + openrtb.Bid //base + VastXML string `json:"vastXml,omitempty"` + VastURL string `json:"vastUrl,omitempty"` +} + +type gammaSeatBid struct { + Bid []gammaBid `json:"bid"` + Group int8 `json:"group,omitempty"` + Ext json.RawMessage `json:"ext,omitempty"` +} +type gammaBidResponse struct { + ID string `json:"id"` + SeatBid []gammaSeatBid `json:"seatbid,omitempty"` + BidID string `json:"bidid,omitempty"` + Cur string `json:"cur,omitempty"` + CustomData string `json:"customdata,omitempty"` + NBR *openrtb.NoBidReasonCode `json:"nbr,omitempty"` + Ext json.RawMessage `json:"ext,omitempty"` +} + func checkParams(gammaExt openrtb_ext.ExtImpGamma) error { if gammaExt.PartnerID == "" { return &errortypes.BadInput{ @@ -180,6 +201,28 @@ func (a *GammaAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapte return adapterRequests, errs } +func convertBid(gBid gammaBid, mediaType openrtb_ext.BidType) *openrtb.Bid { + var bid openrtb.Bid + bid = gBid.Bid + + if mediaType == openrtb_ext.BidTypeVideo { + //Return inline VAST XML Document (Section 6.4.2) + if len(gBid.VastXML) > 0 { + if len(gBid.VastURL) > 0 { + bid.NURL = gBid.VastURL + } + bid.AdM = gBid.VastXML + } else { + return nil + } + } else { + if len(gBid.Bid.AdM) == 0 { + return nil + } + } + return &bid +} + func (a *GammaAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { if response.StatusCode == http.StatusNoContent { return nil, nil @@ -197,24 +240,38 @@ func (a *GammaAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalReq }} } - var bidResp openrtb.BidResponse - if err := json.Unmarshal(response.Body, &bidResp); err != nil { + var gammaResp gammaBidResponse + if err := json.Unmarshal(response.Body, &gammaResp); err != nil { return nil, []error{&errortypes.BadServerResponse{ Message: fmt.Sprintf("bad server response: %d. ", err), }} } - bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) - for _, sb := range bidResp.SeatBid { + //(Section 7.1 No-Bid Signaling) + if len(gammaResp.SeatBid) == 0 { + return nil, nil + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(gammaResp.SeatBid[0].Bid)) + errs := make([]error, 0, len(gammaResp.SeatBid[0].Bid)) + for _, sb := range gammaResp.SeatBid { for i := range sb.Bid { - bid := sb.Bid[i] - bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: getMediaTypeForImp(bidResp.ID, internalRequest.Imp), - }) + mediaType := getMediaTypeForImp(gammaResp.ID, internalRequest.Imp) + bid := convertBid(sb.Bid[i], mediaType) + if bid != nil { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: bid, + BidType: mediaType, + }) + } else { + err := &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Missing Ad Markup. Run with request.debug = 1 for more info"), + } + errs = append(errs, err) + } } } - return bidResponse, nil + return bidResponse, errs } //Adding header fields to request header diff --git a/adapters/gamma/gammatest/exemplary/banner-and-video-and-audio.json b/adapters/gamma/gammatest/exemplary/banner-and-video-and-audio.json index 1c92ab96ffe..4282ac32e4d 100644 --- a/adapters/gamma/gammatest/exemplary/banner-and-video-and-audio.json +++ b/adapters/gamma/gammatest/exemplary/banner-and-video-and-audio.json @@ -80,7 +80,7 @@ "mockResponse": { "status": 200, "body": { - "id": "test-request-id", + "id": "test-imp-video-id", "cur": "USD", "seatbid": [ { @@ -89,7 +89,10 @@ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-video-id", "price": 0.500000, - "adm": "some-test-ad", + "adm": "", + "adomain": ["sample.com"], + "vastXml": "some-test-ad", + "vastUrl": "some-test-url", "crid": "29484110", "w": 640, "h": 480 @@ -122,13 +125,15 @@ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-video-id", "price": 0.5, - "adm": "some-test-ad", + "adm": "", + "vastXml": "some-test-ad", + "vastUrl": "some-test-url", "adid": "29484110", "adomain": ["sample.com"], "cid": "958", "crid": "29484110", - "w": 1024, - "h": 576 + "w": 640, + "h": 480 }, "type": "video" } diff --git a/adapters/gamma/gammatest/exemplary/valid-full-params.json b/adapters/gamma/gammatest/exemplary/valid-full-params.json index 8fa1d3e700a..25d51a646ff 100644 --- a/adapters/gamma/gammatest/exemplary/valid-full-params.json +++ b/adapters/gamma/gammatest/exemplary/valid-full-params.json @@ -2,7 +2,7 @@ "mockBidRequest": { "id": "test-request-id", "app":{ - "id":"test-app-id", + "id":"test-app-id", "name":"test-app-name", "bundle":"test-app-bundle" }, diff --git a/adapters/gamma/gammatest/supplemental/bad-request.json b/adapters/gamma/gammatest/supplemental/bad-request.json index d460550c198..8b533f91de0 100644 --- a/adapters/gamma/gammatest/supplemental/bad-request.json +++ b/adapters/gamma/gammatest/supplemental/bad-request.json @@ -2,7 +2,7 @@ "mockBidRequest": { "id": "test-request-id", "app":{ - "id":"test-app-id", + "id":"test-app-id", "name":"test-app-name", "bundle":"test-app-bundle" }, diff --git a/adapters/gamma/gammatest/supplemental/missing-adm.json b/adapters/gamma/gammatest/supplemental/missing-adm.json new file mode 100644 index 00000000000..6b8ff64c04a --- /dev/null +++ b/adapters/gamma/gammatest/supplemental/missing-adm.json @@ -0,0 +1,76 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-video-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "id": "sample-id", + "zid": "sample-zone-id", + "wid": "sample-web-id" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://hb.gammaplatform.com/adx/request/?id=sample-id&zid=sample-zone-id&wid=sample-web-id&bidid=test-imp-video-id&hb=pbmobile" + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "gamma", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-video-id", + "price": 0.500000, + "crid": "29484110", + "w": 640, + "h": 360 + } + ] + } + ] + } + } + } + ], + "expectedBids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-video-id", + "price": 0.500000, + "crid": "29484110", + "w": 640, + "h": 360 + }, + "type": "video" + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Missing Ad Markup. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/gamma/gammatest/supplemental/missing-param.json b/adapters/gamma/gammatest/supplemental/missing-param.json index 32e4f9eae6d..9dac0936bd7 100644 --- a/adapters/gamma/gammatest/supplemental/missing-param.json +++ b/adapters/gamma/gammatest/supplemental/missing-param.json @@ -20,7 +20,7 @@ "bidder": { "zid": "sample-zone-id", "wid": "sample-web-id" - + } } diff --git a/adapters/gamma/gammatest/supplemental/missing-zone.json b/adapters/gamma/gammatest/supplemental/missing-zone.json index b53ddc83f98..5c84ef229ee 100644 --- a/adapters/gamma/gammatest/supplemental/missing-zone.json +++ b/adapters/gamma/gammatest/supplemental/missing-zone.json @@ -20,7 +20,7 @@ "bidder": { "id": "sample-id", "wid": "sample-web-id" - + } } diff --git a/adapters/gamma/gammatest/supplemental/nobid-signaling.json b/adapters/gamma/gammatest/supplemental/nobid-signaling.json new file mode 100644 index 00000000000..4055ad70249 --- /dev/null +++ b/adapters/gamma/gammatest/supplemental/nobid-signaling.json @@ -0,0 +1,56 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app":{ + "id":"test-app-id", + "name":"test-app-name", + "bundle":"test-app-bundle" + }, + + "device":{ + "ua":"test-device-ua", + "ip":"test-device-ip", + "ifa":"test-device-ifa", + "model":"test-device-model", + "os":"test-device-os", + "dnt":1 + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext":{ + "bidder":{ + "id": "sample-id", + "zid": "sample-zone-id", + "wid": "sample-web-id" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://hb.gammaplatform.com/adx/request/?id=sample-id&zid=sample-zone-id&wid=sample-web-id&bidid=test-imp-id&hb=pbmobile&device_ip=test-device-ip&device_model=test-device-model&device_os=test-device-os&device_ua=test-device-ua&device_ifa=test-device-ifa&app_id=test-app-id&app_bundle=test-app-bundle&app_name=test-app-name" + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + ] + } + } + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/gamma/gammatest/supplemental/status-forbidden.json b/adapters/gamma/gammatest/supplemental/status-forbidden.json index 950a45ec0c9..3a30b210f4f 100644 --- a/adapters/gamma/gammatest/supplemental/status-forbidden.json +++ b/adapters/gamma/gammatest/supplemental/status-forbidden.json @@ -2,7 +2,7 @@ "mockBidRequest": { "id": "test-request-id", "app":{ - "id":"test-app-id", + "id":"test-app-id", "name":"test-app-name", "bundle":"test-app-bundle" }, diff --git a/adapters/gamma/gammatest/supplemental/status-no-content.json b/adapters/gamma/gammatest/supplemental/status-no-content.json index 6c7878a86c9..045fb939ced 100644 --- a/adapters/gamma/gammatest/supplemental/status-no-content.json +++ b/adapters/gamma/gammatest/supplemental/status-no-content.json @@ -2,7 +2,7 @@ "mockBidRequest": { "id": "test-request-id", "app":{ - "id":"test-app-id", + "id":"test-app-id", "name":"test-app-name", "bundle":"test-app-bundle" }, From ebdf997cc1c010cb35d5df90b2241f03c074882d Mon Sep 17 00:00:00 2001 From: gpolaert Date: Fri, 28 Aug 2020 19:40:28 +0200 Subject: [PATCH 183/318] fix: avoid unexpected EOF on gz writer (#1449) --- .../pubstack/eventchannel/eventchannel.go | 10 ++--- .../eventchannel/eventchannel_test.go | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/analytics/pubstack/eventchannel/eventchannel.go b/analytics/pubstack/eventchannel/eventchannel.go index b8dc4dd8e28..d9c2bc4117c 100644 --- a/analytics/pubstack/eventchannel/eventchannel.go +++ b/analytics/pubstack/eventchannel/eventchannel.go @@ -93,10 +93,13 @@ func (c *EventChannel) flush() { return } + // reset buffers and writers + defer c.reset() + // finish writing gzip header - err := c.gz.Flush() + err := c.gz.Close() if err != nil { - glog.Warning("[pubstack] fail to flush gzipped buffer") + glog.Warning("[pubstack] fail to close gzipped buffer") return } @@ -108,9 +111,6 @@ func (c *EventChannel) flush() { return } - // reset buffers and writers - c.reset() - // send events (async) go c.send(payload) } diff --git a/analytics/pubstack/eventchannel/eventchannel_test.go b/analytics/pubstack/eventchannel/eventchannel_test.go index 9fdcfe976a6..792e15e151e 100644 --- a/analytics/pubstack/eventchannel/eventchannel_test.go +++ b/analytics/pubstack/eventchannel/eventchannel_test.go @@ -134,3 +134,42 @@ func TestEventChannel_Push(t *testing.T) { assert.Equal(t, string(data), "onetwothreefourfivesixseven") } + +func TestEventChannel_OutputFormat(t *testing.T) { + + toGzip := func(payload string) []byte { + var buf bytes.Buffer + zw := gzip.NewWriter(&buf) + + if _, err := zw.Write([]byte(payload)); err != nil { + assert.Fail(t, err.Error()) + } + + if err := zw.Close(); err != nil { + assert.Fail(t, err.Error()) + } + return buf.Bytes() + } + + data := make([]byte, 0) + send := func(payload []byte) error { + data = append(data, payload...) + return nil + } + + eventChannel := NewEventChannel(send, 15000, 10, 2*time.Minute) + + eventChannel.Push([]byte("one")) + eventChannel.flush() + eventChannel.Push([]byte("two")) + eventChannel.Push([]byte("three")) + + eventChannel.Close() + + time.Sleep(10 * time.Millisecond) + + expected := append(toGzip("one"), toGzip("twothree")...) + + assert.Equal(t, expected, data) + +} From d8dc27f245d603af9b3f58fb5897b61e417b2d3c Mon Sep 17 00:00:00 2001 From: Stephan Brosinski Date: Tue, 1 Sep 2020 01:02:15 +0200 Subject: [PATCH 184/318] Smaato adapter: support for video mediaType (#1463) Co-authored-by: vikram --- adapters/smaato/smaato.go | 59 ++++-- .../smaato/smaatotest/exemplary/video.json | 187 ++++++++++++++++++ .../supplemental/bad-adm-response.json | 8 +- static/bidder-info/smaato.yaml | 4 +- 4 files changed, 244 insertions(+), 14 deletions(-) create mode 100644 adapters/smaato/smaatotest/exemplary/video.json diff --git a/adapters/smaato/smaato.go b/adapters/smaato/smaato.go index 06678d77a61..b48851bb4d2 100644 --- a/adapters/smaato/smaato.go +++ b/adapters/smaato/smaato.go @@ -20,6 +20,7 @@ type adMarkupType string const ( smtAdTypeImg adMarkupType = "Img" smtAdTypeRichmedia adMarkupType = "Richmedia" + smtAdTypeVideo adMarkupType = "Video" ) // SmaatoAdapter describes a Smaato prebid server adapter. @@ -63,7 +64,7 @@ func (a *SmaatoAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapt } // Use bidRequestExt of first imp to retrieve params which are valid for all imps, e.g. publisherId - publisherId, err := jsonparser.GetString(request.Imp[0].Ext, "bidder", "publisherId") + publisherID, err := jsonparser.GetString(request.Imp[0].Ext, "bidder", "publisherId") if err != nil { errs = append(errs, err) return nil, errs @@ -80,7 +81,7 @@ func (a *SmaatoAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapt } if request.Site != nil { siteCopy := *request.Site - siteCopy.Publisher = &openrtb.Publisher{ID: publisherId} + siteCopy.Publisher = &openrtb.Publisher{ID: publisherID} if request.Site.Ext != nil { var siteExt siteExt @@ -178,16 +179,25 @@ func (a *SmaatoAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRe for i := 0; i < len(sb.Bid); i++ { bid := sb.Bid[i] + markupType, markupTypeErr := getAdMarkupType(response, bid.AdM) + if markupTypeErr != nil { + return nil, []error{markupTypeErr} + } + var markupError error - bid.AdM, markupError = renderAdMarkup(getAdMarkupType(response, bid.AdM), bid.AdM) + bid.AdM, markupError = renderAdMarkup(markupType, bid.AdM) if markupError != nil { - fmt.Println(markupError) - continue // no bid when broken ad markup + return nil, []error{markupError} + } + + bidType, bidTypeErr := markupTypeToBidType(markupType) + if bidTypeErr != nil { + return nil, []error{bidTypeErr} } bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &bid, - BidType: openrtb_ext.BidTypeBanner, + BidType: bidType, }) } } @@ -202,23 +212,41 @@ func renderAdMarkup(adMarkupType adMarkupType, adMarkup string) (string, error) adm, markupError = extractAdmImage(adMarkup) case smtAdTypeRichmedia: adm, markupError = extractAdmRichMedia(adMarkup) + case smtAdTypeVideo: + adm, markupError = adMarkup, nil default: return "", fmt.Errorf("Unknown markup type %s", adMarkupType) } return adm, markupError } -func getAdMarkupType(response *adapters.ResponseData, adMarkup string) adMarkupType { +func markupTypeToBidType(markupType adMarkupType) (openrtb_ext.BidType, error) { + switch markupType { + case smtAdTypeImg: + return openrtb_ext.BidTypeBanner, nil + case smtAdTypeRichmedia: + return openrtb_ext.BidTypeBanner, nil + case smtAdTypeVideo: + return openrtb_ext.BidTypeVideo, nil + default: + return "", fmt.Errorf("Invalid markupType %s", markupType) + } +} + +func getAdMarkupType(response *adapters.ResponseData, adMarkup string) (adMarkupType, error) { if admType := adMarkupType(response.Headers.Get("X-SMT-ADTYPE")); admType != "" { - return admType + return admType, nil } if strings.HasPrefix(adMarkup, `{"image":`) { - return smtAdTypeImg + return smtAdTypeImg, nil } if strings.HasPrefix(adMarkup, `{"richmedia":`) { - return smtAdTypeRichmedia + return smtAdTypeRichmedia, nil + } + if strings.HasPrefix(adMarkup, `", + "adomain": [ + "smaato.com" + ], + "bidderName": "smaato", + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://iurl", + "nurl": "https://nurl", + "price": 0.01, + "w": 1024, + "h": 768 + } + ] + } + ], + "bidid": "04db8629-179d-4bcd-acce-e54722969006", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adm": "", + "adomain": [ + "smaato.com" + ], + "cid": "CM6523", + "crid": "CR69381", + "id": "6906aae8-7f74-4edd-9a4f-f49379a3cadd", + "impid": "1C86242D-9535-47D6-9576-7B1FE87F282C", + "iurl": "https://iurl", + "nurl": "https://nurl", + "price": 0.01, + "w": 1024, + "h": 768 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/smaato/smaatotest/supplemental/bad-adm-response.json b/adapters/smaato/smaatotest/supplemental/bad-adm-response.json index 6d4990e9ea4..1fce58f0dfe 100644 --- a/adapters/smaato/smaatotest/supplemental/bad-adm-response.json +++ b/adapters/smaato/smaatotest/supplemental/bad-adm-response.json @@ -162,5 +162,11 @@ } } ], - "expectedBidResponses": [] + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Invalid ad markup {\"badmedia\":{\"mediadata\":{\"content\":\"

\", \"w\":350,\"h\":50},\"impressiontrackers\":[\"//prebid-test.smaatolabs.net/track/imp/1\",\"//prebid-test.smaatolabs.net/track/imp/2\"],\"clicktrackers\":[\"//prebid-test.smaatolabs.net/track/click/1\",\"//prebid-test.smaatolabs.net/track/click/2\"]}}", + "comparison": "literal" + } + ] } \ No newline at end of file diff --git a/static/bidder-info/smaato.yaml b/static/bidder-info/smaato.yaml index 662603febdb..db3e61e5cc6 100644 --- a/static/bidder-info/smaato.yaml +++ b/static/bidder-info/smaato.yaml @@ -4,6 +4,8 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - - banner \ No newline at end of file + - banner + - video From 412e0fcf4fa43f07a3aec843373e5c093093f43b Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Tue, 1 Sep 2020 19:02:39 +0300 Subject: [PATCH 185/318] Rubicon liveramp param (#1466) Add liveramp mapping to user.ext should translate the "liveramp.com" id from the "user.ext.eids" array to "user.ext.liveramp_idl" as follows: ``` { "user": { "ext": { "eids": [{ "source": 'liveramp.com', "uids": [{ "id": "T7JiRRvsRAmh88" }] }] } } } ``` to XAPI: ``` { "user": { "ext": { "liveramp_idl": "T7JiRRvsRAmh88" } } } ``` --- adapters/rubicon/rubicon.go | 60 ++++++++++++++++++++++---------- adapters/rubicon/rubicon_test.go | 14 +++++++- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index a69530de831..56ae7b2f792 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -93,11 +93,12 @@ type rubiconExtUserTpID struct { } type rubiconUserExt struct { - Consent string `json:"consent,omitempty"` - DigiTrust *openrtb_ext.ExtUserDigiTrust `json:"digitrust"` - Eids []openrtb_ext.ExtUserEid `json:"eids,omitempty"` - TpID []rubiconExtUserTpID `json:"tpid,omitempty"` - RP rubiconUserExtRP `json:"rp"` + Consent string `json:"consent,omitempty"` + DigiTrust *openrtb_ext.ExtUserDigiTrust `json:"digitrust"` + Eids []openrtb_ext.ExtUserEid `json:"eids,omitempty"` + TpID []rubiconExtUserTpID `json:"tpid,omitempty"` + RP rubiconUserExtRP `json:"rp"` + LiverampIdl string `json:"liveramp_idl,omitempty"` } type rubiconSiteExtRP struct { @@ -247,6 +248,12 @@ type rubiconUserExtEidUidExt struct { RtiPartner string `json:"rtiPartner,omitempty"` } +type mappedRubiconUidsParam struct { + tpIds []rubiconExtUserTpID + segments []string + liverampIdl string +} + //MAS algorithm func findPrimary(alt []int) (int, []int) { min, pos, primary := 0, 0, 0 @@ -683,13 +690,18 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap // set user.ext.tpid if len(userExt.Eids) > 0 { - if tpIds, segments, errors := getTpIdsAndSegments(userExt.Eids); len(errors) > 0 { + mappedRubiconUidsParam, errors := getTpIdsAndSegments(userExt.Eids) + if len(errors) > 0 { errs = append(errs, errors...) continue - } else if err := updateUserExtWithTpIdsAndSegments(&userExtRP, tpIds, segments); err != nil { + } + + if err := updateUserExtWithTpIdsAndSegments(&userExtRP, mappedRubiconUidsParam); err != nil { errs = append(errs, err) continue } + + userExtRP.LiverampIdl = mappedRubiconUidsParam.liverampIdl } } @@ -793,9 +805,11 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap return requestData, errs } -func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, []string, []error) { - tpIds := make([]rubiconExtUserTpID, 0) - segments := make([]string, 0) +func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) (mappedRubiconUidsParam, []error) { + rubiconUidsParam := mappedRubiconUidsParam{ + tpIds: make([]rubiconExtUserTpID, 0), + segments: make([]string, 0), + } errs := make([]error, 0) for _, eid := range eids { @@ -815,7 +829,7 @@ func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, [ } if eidUidExt.RtiPartner == "TDID" { - tpIds = append(tpIds, rubiconExtUserTpID{Source: "tdid", UID: uid.ID}) + rubiconUidsParam.tpIds = append(rubiconUidsParam.tpIds, rubiconExtUserTpID{Source: "tdid", UID: uid.ID}) } } } @@ -824,7 +838,7 @@ func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, [ if len(uids) > 0 { uidId := uids[0].ID if uidId != "" { - tpIds = append(tpIds, rubiconExtUserTpID{Source: "liveintent.com", UID: uidId}) + rubiconUidsParam.tpIds = append(rubiconUidsParam.tpIds, rubiconExtUserTpID{Source: "liveintent.com", UID: uidId}) } if eid.Ext != nil { @@ -835,20 +849,28 @@ func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, [ }) continue } - segments = eidExt.Segments + rubiconUidsParam.segments = eidExt.Segments + } + } + case "liveramp.com": + uids := eid.Uids + if len(uids) > 0 { + uidId := uids[0].ID + if uidId != "" && rubiconUidsParam.liverampIdl == "" { + rubiconUidsParam.liverampIdl = uidId } } } } - return tpIds, segments, errs + return rubiconUidsParam, errs } -func updateUserExtWithTpIdsAndSegments(userExtRP *rubiconUserExt, tpIds []rubiconExtUserTpID, segments []string) error { - if len(tpIds) > 0 { - userExtRP.TpID = tpIds +func updateUserExtWithTpIdsAndSegments(userExtRP *rubiconUserExt, rubiconUidsParam mappedRubiconUidsParam) error { + if len(rubiconUidsParam.tpIds) > 0 { + userExtRP.TpID = rubiconUidsParam.tpIds - if segments != nil { + if rubiconUidsParam.segments != nil { userExtRPTarget := make(map[string]interface{}) if userExtRP.RP.Target != nil { @@ -857,7 +879,7 @@ func updateUserExtWithTpIdsAndSegments(userExtRP *rubiconUserExt, tpIds []rubico } } - userExtRPTarget["LIseg"] = segments + userExtRPTarget["LIseg"] = rubiconUidsParam.segments if target, err := json.Marshal(&userExtRPTarget); err != nil { return &errortypes.BadInput{Message: err.Error()} diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index 0489797561b..5ec78ccf4f3 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -1219,6 +1219,15 @@ func TestOpenRTBRequestWithSpecificExtUserEids(t *testing.T) { "ext": { "segments": ["999","888"] } + }, + { + "source": "liveramp.com", + "uids": [{ + "id": "LIVERAMPID" + }], + "ext": { + "segments": ["111","222"] + } } ]}`), }, @@ -1239,7 +1248,7 @@ func TestOpenRTBRequestWithSpecificExtUserEids(t *testing.T) { } assert.NotNil(t, userExt.Eids) - assert.Equal(t, 3, len(userExt.Eids), "Eids values are not as expected!") + assert.Equal(t, 4, len(userExt.Eids), "Eids values are not as expected!") assert.NotNil(t, userExt.TpID) assert.Equal(t, 2, len(userExt.TpID), "TpID values are not as expected!") @@ -1250,6 +1259,9 @@ func TestOpenRTBRequestWithSpecificExtUserEids(t *testing.T) { // liveintent.com assert.Equal(t, "liveintent.com", userExt.TpID[1].Source, "TpID source value is not as expected!") + // liveramp.com + assert.Equal(t, "LIVERAMPID", userExt.LiverampIdl, "Liveramp_idl value is not as expected!") + userExtRPTarget := make(map[string]interface{}) if err := json.Unmarshal(userExt.RP.Target, &userExtRPTarget); err != nil { t.Fatal("Error unmarshalling request.user.ext.rp.target object.") From 754de04fee53656f669bf5cc52251c688f5a36fa Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Tue, 1 Sep 2020 13:09:32 -0700 Subject: [PATCH 186/318] Consolidate StoredRequest configs, add validation for all data types (#1453) --- config/config.go | 51 ++++-- config/config_test.go | 47 ++++- config/stored_requests.go | 253 ++++++++------------------ config/stored_requests_test.go | 84 ++++++++- stored_requests/config/config.go | 97 +++------- stored_requests/config/config_test.go | 108 +++-------- 6 files changed, 284 insertions(+), 356 deletions(-) diff --git a/config/config.go b/config/config.go index c2e7dfd8f3f..e443a48ec1d 100755 --- a/config/config.go +++ b/config/config.go @@ -29,18 +29,19 @@ type Configuration struct { EnableGzip bool `mapstructure:"enable_gzip"` // StatusResponse is the string which will be returned by the /status endpoint when things are OK. // If empty, it will return a 204 with no content. - StatusResponse string `mapstructure:"status_response"` - AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"` - CacheURL Cache `mapstructure:"cache"` - ExtCacheURL ExternalCache `mapstructure:"external_cache"` - RecaptchaSecret string `mapstructure:"recaptcha_secret"` - HostCookie HostCookie `mapstructure:"host_cookie"` - Metrics Metrics `mapstructure:"metrics"` - DataCache DataCache `mapstructure:"datacache"` - StoredRequests StoredRequests `mapstructure:"stored_requests"` - CategoryMapping StoredRequestsSlim `mapstructure:"category_mapping"` + StatusResponse string `mapstructure:"status_response"` + AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"` + CacheURL Cache `mapstructure:"cache"` + ExtCacheURL ExternalCache `mapstructure:"external_cache"` + RecaptchaSecret string `mapstructure:"recaptcha_secret"` + HostCookie HostCookie `mapstructure:"host_cookie"` + Metrics Metrics `mapstructure:"metrics"` + DataCache DataCache `mapstructure:"datacache"` + StoredRequests StoredRequests `mapstructure:"stored_requests"` + StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"` + CategoryMapping StoredRequests `mapstructure:"category_mapping"` // Note that StoredVideo refers to stored video requests, and has nothing to do with caching video creatives. - StoredVideo StoredRequestsSlim `mapstructure:"stored_video_req"` + StoredVideo StoredRequests `mapstructure:"stored_video_req"` // Adapters should have a key for every openrtb_ext.BidderName, converted to lower-case. // Se also: https://github.com/spf13/viper/issues/371#issuecomment-335388559 @@ -103,7 +104,10 @@ func (c configErrors) Error() string { func (cfg *Configuration) validate() configErrors { var errs configErrors errs = cfg.AuctionTimeouts.validate(errs) - errs = cfg.StoredRequests.validate(errs) + errs = cfg.StoredRequests.validate("stored_req", errs) + errs = cfg.StoredRequestsAMP.validate("stored_amp_req", errs) + errs = cfg.CategoryMapping.validate("categories", errs) + errs = cfg.StoredVideo.validate("stored_video_req", errs) errs = cfg.Metrics.validate(errs) if cfg.MaxRequestSize < 0 { errs = append(errs, fmt.Errorf("cfg.max_request_size must be >= 0. Got %d", cfg.MaxRequestSize)) @@ -604,6 +608,9 @@ func New(v *viper.Viper) (*Configuration, error) { c.BlacklistedAcctMap[c.BlacklistedAccts[i]] = true } + // Migrate combo stored request config to separate stored_reqs and amp stored_reqs configs. + resolvedStoredRequestsConfig(&c) + glog.Info("Logging the resolved configuration:") logGeneral(reflect.ValueOf(c), " \t") if errs := c.validate(); len(errs) > 0 { @@ -721,6 +728,7 @@ func SetupViper(v *viper.Viper, filename string) { v.AddConfigPath(".") v.AddConfigPath("/etc/config") } + // Fixes #475: Some defaults will be set just so they are accessible via environment variables // (basically so viper knows they exist) v.SetDefault("external_url", "http://localhost:8000") @@ -779,7 +787,8 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("category_mapping.filesystem.enabled", true) v.SetDefault("category_mapping.filesystem.directorypath", "./static/category-mapping") v.SetDefault("category_mapping.http.endpoint", "") - v.SetDefault("stored_requests.filesystem", false) + v.SetDefault("stored_requests.filesystem.enabled", false) + v.SetDefault("stored_requests.filesystem.directorypath", "./stored_requests/data/by_id") v.SetDefault("stored_requests.directorypath", "./stored_requests/data/by_id") v.SetDefault("stored_requests.postgres.connection.dbname", "") v.SetDefault("stored_requests.postgres.connection.host", "") @@ -995,6 +1004,22 @@ func SetupViper(v *viper.Viper, filename string) { v.SetEnvPrefix("PBS") v.AutomaticEnv() v.ReadInConfig() + + // Migrate config settings to maintain compatibility with old configs + migrateConfig(v) +} + +func migrateConfig(v *viper.Viper) { + // if stored_requests.filesystem is not a map in conf file as expected from defaults, + // means we have old-style settings; migrate them to new filesystem map to avoid breaking viper + if _, ok := v.Get("stored_requests.filesystem").(map[string]interface{}); !ok { + glog.Warning("stored_requests.filesystem should be changed to stored_requests.filesystem.enabled") + glog.Warning("stored_requests.directorypath should be changed to stored_requests.filesystem.directorypath") + m := v.GetStringMap("stored_requests.filesystem") + m["enabled"] = v.GetBool("stored_requests.filesystem") + m["directorypath"] = v.GetString("stored_requests.directorypath") + v.Set("stored_requests.filesystem", m) + } } func setBidderDefaults(v *viper.Viper, bidder string) { diff --git a/config/config_test.go b/config/config_test.go index 40589ac7b23..b23ddd6f614 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -3,6 +3,7 @@ package config import ( "bytes" "net" + "os" "strings" "testing" "time" @@ -135,6 +136,8 @@ func TestDefaults(t *testing.T) { cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, false) cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true) cmpStrings(t, "certificates_file", cfg.PemCertsFile, "") + cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled) + cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path) } var fullConfig = []byte(` @@ -287,6 +290,12 @@ adapters: usersync_url: http:\\tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r= `) +var oldStoredRequestsConfig = []byte(` +stored_requests: + filesystem: true + directorypath: "/somepath" +`) + func cmpStrings(t *testing.T, key string, a string, b string) { t.Helper() assert.Equal(t, a, b, "%s: %s != %s", key, a, b) @@ -440,17 +449,53 @@ func TestUnmarshalAdapterExtraInfo(t *testing.T) { func TestValidConfig(t *testing.T) { cfg := Configuration{ StoredRequests: StoredRequests{ - Files: true, + Files: FileFetcherConfig{Enabled: true}, + InMemoryCache: InMemoryCache{ + Type: "none", + }, + }, + StoredVideo: StoredRequests{ + Files: FileFetcherConfig{Enabled: true}, InMemoryCache: InMemoryCache{ Type: "none", }, }, + CategoryMapping: StoredRequests{ + Files: FileFetcherConfig{Enabled: true}, + }, } + resolvedStoredRequestsConfig(&cfg) err := cfg.validate() assert.Nil(t, err, "OpenRTB filesystem config should work. %v", err) } +func TestMigrateConfig(t *testing.T) { + v := viper.New() + SetupViper(v, "") + v.SetConfigType("yaml") + v.ReadConfig(bytes.NewBuffer(oldStoredRequestsConfig)) + migrateConfig(v) + cfg, err := New(v) + assert.NoError(t, err, "Setting up config should work but it doesn't") + cmpBools(t, "stored_requests.filesystem.enabled", true, cfg.StoredRequests.Files.Enabled) + cmpStrings(t, "stored_requests.filesystem.path", "/somepath", cfg.StoredRequests.Files.Path) +} + +func TestMigrateConfigFromEnv(t *testing.T) { + if oldval, ok := os.LookupEnv("PBS_STORED_REQUESTS_FILESYSTEM"); ok { + defer os.Setenv("PBS_STORED_REQUESTS_FILESYSTEM", oldval) + } else { + defer os.Unsetenv("PBS_STORED_REQUESTS_FILESYSTEM") + } + os.Setenv("PBS_STORED_REQUESTS_FILESYSTEM", "true") + v := viper.New() + SetupViper(v, "") + cfg, err := New(v) + assert.NoError(t, err, "Setting up config should work but it doesn't") + cmpBools(t, "stored_requests.filesystem.enabled", true, cfg.StoredRequests.Files.Enabled) +} + func TestInvalidAdapterEndpointConfig(t *testing.T) { v := viper.New() SetupViper(v, "") diff --git a/config/stored_requests.go b/config/stored_requests.go index 04e400f9b7c..b63073fede7 100644 --- a/config/stored_requests.go +++ b/config/stored_requests.go @@ -2,7 +2,6 @@ package config import ( "bytes" - "errors" "fmt" "strconv" "strings" @@ -11,44 +10,35 @@ import ( "github.com/golang/glog" ) -// StoredRequests configures the backend used to store requests on the server. -type StoredRequests struct { - // Files should be true if Stored Requests should be loaded from the filesystem. - Files bool `mapstructure:"filesystem"` - //If data should be loaded from file system, path should be specified in configuration - Path string `mapstructure:"directorypath"` - // Postgres configures Fetchers and EventProducers which read from a Postgres DB. - // Fetchers are in stored_requests/backends/db_fetcher/postgres.go - // EventProducers are in stored_requests/events/postgres - Postgres PostgresConfig `mapstructure:"postgres"` - // HTTP configures an instance of stored_requests/backends/http/http_fetcher.go. - // If non-nil, Stored Requests will be fetched from the endpoint described there. - HTTP HTTPFetcherConfig `mapstructure:"http"` - // InMemoryCache configures an instance of stored_requests/caches/memory/cache.go. - // If non-nil, Stored Requests will be saved in an in-memory cache. - InMemoryCache InMemoryCache `mapstructure:"in_memory_cache"` - // CacheEventsAPI configures an instance of stored_requests/events/api/api.go. - // If non-nil, Stored Request Caches can be updated or invalidated through API endpoints. - // This is intended to be a useful development tool and not recommended for a production environment. - // It should not be exposed to public networks without authentication. - CacheEventsAPI bool `mapstructure:"cache_events_api"` - // HTTPEvents configures an instance of stored_requests/events/http/http.go. - // If non-nil, the server will use those endpoints to populate and update the cache. - HTTPEvents HTTPEventsConfig `mapstructure:"http_events"` +// DataType constants +type DataType string + +const ( + RequestDataType DataType = "Request" + CategoryDataType DataType = "Category" + VideoDataType DataType = "Video" + AMPRequestDataType DataType = "AMP Request" +) + +func (sr *StoredRequests) DataType() DataType { + return sr.dataType } -// StoredRequestsSlim struct defines options for stored requests from a single endpoint -type StoredRequestsSlim struct { +// StoredRequests struct defines options for stored requests for each data type +// including some amp stored_requests options +type StoredRequests struct { + // dataType is a tag pushed from upstream indicating the type of object fetched here + dataType DataType // Files should be used if Stored Requests should be loaded from the filesystem. // Fetchers are in stored_requests/backends/file_system/fetcher.go Files FileFetcherConfig `mapstructure:"filesystem"` // Postgres configures Fetchers and EventProducers which read from a Postgres DB. // Fetchers are in stored_requests/backends/db_fetcher/postgres.go // EventProducers are in stored_requests/events/postgres - Postgres PostgresConfigSlim `mapstructure:"postgres"` + Postgres PostgresConfig `mapstructure:"postgres"` // HTTP configures an instance of stored_requests/backends/http/http_fetcher.go. // If non-nil, Stored Requests will be fetched from the endpoint described there. - HTTP HTTPFetcherConfigSlim `mapstructure:"http"` + HTTP HTTPFetcherConfig `mapstructure:"http"` // InMemoryCache configures an instance of stored_requests/caches/memory/cache.go. // If non-nil, Stored Requests will be saved in an in-memory cache. InMemoryCache InMemoryCache `mapstructure:"in_memory_cache"` @@ -57,14 +47,7 @@ type StoredRequestsSlim struct { CacheEvents CacheEventsConfig `mapstructure:"cache_events"` // HTTPEvents configures an instance of stored_requests/events/http/http.go. // If non-nil, the server will use those endpoints to populate and update the cache. - HTTPEvents HTTPEventsConfigSlim `mapstructure:"http_events"` -} - -// HTTPEventsConfigSlim configures stored_requests/events/http/http.go -type HTTPEventsConfigSlim struct { - Endpoint string `mapstructure:"endpoint"` - RefreshRate int64 `mapstructure:"refresh_rate_seconds"` - Timeout int `mapstructure:"timeout_ms"` + HTTPEvents HTTPEventsConfig `mapstructure:"http_events"` } // HTTPEventsConfig configures stored_requests/events/http/http.go @@ -83,14 +66,6 @@ func (cfg HTTPEventsConfig) RefreshRateDuration() time.Duration { return time.Duration(cfg.RefreshRate) * time.Second } -func (cfg HTTPEventsConfigSlim) TimeoutDuration() time.Duration { - return time.Duration(cfg.Timeout) * time.Millisecond -} - -func (cfg HTTPEventsConfigSlim) RefreshRateDuration() time.Duration { - return time.Duration(cfg.RefreshRate) * time.Second -} - // CacheEventsConfig configured stored_requests/events/api/api.go type CacheEventsConfig struct { // Enabled should be true to enable the events api endpoint @@ -107,48 +82,64 @@ type FileFetcherConfig struct { Path string `mapstructure:"directorypath"` } -// HTTPFetcherConfigSlim configures a stored_requests/backends/http_fetcher/fetcher.go -type HTTPFetcherConfigSlim struct { - Endpoint string `mapstructure:"endpoint"` -} - // HTTPFetcherConfig configures a stored_requests/backends/http_fetcher/fetcher.go type HTTPFetcherConfig struct { Endpoint string `mapstructure:"endpoint"` AmpEndpoint string `mapstructure:"amp_endpoint"` } -func (cfg *StoredRequests) validate(errs configErrors) configErrors { +// Migrate combined stored_requests+amp configuration to separate simple config sections +func resolvedStoredRequestsConfig(cfg *Configuration) { + sr := &cfg.StoredRequests + amp := &cfg.StoredRequestsAMP + + sr.CacheEvents.Endpoint = "/storedrequests/openrtb2" // why is this here and not SetDefault ? + + // Amp uses the same config but some fields get replaced by Amp* version of similar fields + cfg.StoredRequestsAMP = cfg.StoredRequests + amp.Postgres.FetcherQueries.QueryTemplate = sr.Postgres.FetcherQueries.AmpQueryTemplate + amp.Postgres.CacheInitialization.Query = sr.Postgres.CacheInitialization.AmpQuery + amp.Postgres.PollUpdates.Query = sr.Postgres.PollUpdates.AmpQuery + amp.HTTP.Endpoint = sr.HTTP.AmpEndpoint + amp.CacheEvents.Endpoint = "/storedrequests/amp" + amp.HTTPEvents.Endpoint = sr.HTTPEvents.AmpEndpoint + + // Set data types for each section + cfg.StoredRequests.dataType = RequestDataType + cfg.StoredRequestsAMP.dataType = AMPRequestDataType + cfg.StoredVideo.dataType = VideoDataType + cfg.CategoryMapping.dataType = CategoryDataType + return +} + +func (cfg *StoredRequests) validate(section string, errs configErrors) configErrors { + errs = cfg.Postgres.validate(section, errs) + + // Categories do not use cache so none of the following checks apply + if cfg.dataType == CategoryDataType { + return errs + } + if cfg.InMemoryCache.Type == "none" { - if cfg.CacheEventsAPI { - errs = append(errs, errors.New("stored_requests.cache_events_api must be false if stored_requests.in_memory_cache=none")) + if cfg.CacheEvents.Enabled { + errs = append(errs, fmt.Errorf("%s: cache_events must be disabled if in_memory_cache=none", section)) } if cfg.HTTPEvents.RefreshRate != 0 { - errs = append(errs, errors.New("stored_requests.http_events.refresh_rate_seconds must be 0 if stored_requests.in_memory_cache=none")) + errs = append(errs, fmt.Errorf("%s: http_events.refresh_rate_seconds must be 0 if in_memory_cache=none", section)) } if cfg.Postgres.PollUpdates.Query != "" { - errs = append(errs, errors.New("stored_requests.postgres.poll_for_updates.query must be empty if stored_requests.in_memory_cache=none")) + errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.query must be empty if in_memory_cache=none", section)) } if cfg.Postgres.CacheInitialization.Query != "" { - errs = append(errs, errors.New("stored_requests.postgres.initialize_caches.query must be empty if stored_requests.in_memory_cache=none")) + errs = append(errs, fmt.Errorf("%s: postgres.initialize_caches.query must be empty if in_memory_cache=none", section)) } } - errs = cfg.InMemoryCache.validate(errs) - errs = cfg.Postgres.validate(errs) + errs = cfg.InMemoryCache.validate(section, errs) return errs } -// PostgresConfigSlim configures the Stored Request ecosystem to use Postgres. This must include a Fetcher, -// and may optionally include some EventProducers to populate and refresh the caches. -type PostgresConfigSlim struct { - ConnectionInfo PostgresConnection `mapstructure:"connection"` - FetcherQueries PostgresFetcherQueriesSlim `mapstructure:"fetcher"` - CacheInitialization PostgresCacheInitializerSlim `mapstructure:"initialize_caches"` - PollUpdates PostgresUpdatePollingSlim `mapstructure:"poll_for_updates"` -} - // PostgresConfig configures the Stored Request ecosystem to use Postgres. This must include a Fetcher, // and may optionally include some EventProducers to populate and refresh the caches. type PostgresConfig struct { @@ -158,12 +149,12 @@ type PostgresConfig struct { PollUpdates PostgresUpdatePolling `mapstructure:"poll_for_updates"` } -func (cfg *PostgresConfig) validate(errs configErrors) configErrors { +func (cfg *PostgresConfig) validate(section string, errs configErrors) configErrors { if cfg.ConnectionInfo.Database == "" { return errs } - return cfg.PollUpdates.validate(errs) + return cfg.PollUpdates.validate(section, errs) } // PostgresConnection has options which put types to the Postgres Connection string. See: @@ -242,32 +233,6 @@ type PostgresFetcherQueries struct { AmpQueryTemplate string `mapstructure:"amp_query"` } -type PostgresFetcherQueriesSlim struct { - // QueryTemplate is the Postgres Query which can be used to fetch configs from the database. - // It is a Template, rather than a full Query, because a single HTTP request may reference multiple Stored Requests. - // - // In the simplest case, this could be something like: - // SELECT id, requestData, 'request' as type - // FROM stored_requests - // WHERE id in %REQUEST_ID_LIST% - // UNION ALL - // SELECT id, impData, 'imp' as type - // FROM stored_imps - // WHERE id in %IMP_ID_LIST% - // - // The MakeQuery function will transform this query into: - // SELECT id, requestData, 'request' as type - // FROM stored_requests - // WHERE id in ($1) - // UNION ALL - // SELECT id, impData, 'imp' as type - // FROM stored_imps - // WHERE id in ($2, $3, $4, ...) - // - // ... where the number of "$x" args depends on how many IDs are nested within the HTTP request. - QueryTemplate string `mapstructure:"query"` -} - type PostgresCacheInitializer struct { Timeout int `mapstructure:"timeout_ms"` // Query should be something like: @@ -284,69 +249,19 @@ type PostgresCacheInitializer struct { AmpQuery string `mapstructure:"amp_query"` } -type PostgresCacheInitializerSlim struct { - Timeout int `mapstructure:"timeout_ms"` - // Query should be something like: - // - // SELECT id, requestData, 'request' AS type FROM stored_requests - // UNION ALL - // SELECT id, impData, 'imp' AS type FROM stored_imps - // - // This query will be run once on startup to fetch _all_ known Stored Request data from the database. - // - // For more details on the expected format of requestData and impData, see stored_requests/events/postgres/polling.go - Query string `mapstructure:"query"` -} - -func (cfg *PostgresCacheInitializerSlim) validate(errs configErrors) configErrors { +func (cfg *PostgresCacheInitializer) validate(section string, errs configErrors) configErrors { if cfg.Query == "" { return errs } if cfg.Timeout <= 0 { - errs = append(errs, errors.New("stored_requests.postgres.initialize_caches.timeout_ms must be positive")) + errs = append(errs, fmt.Errorf("%s: postgres.initialize_caches.timeout_ms must be positive", section)) } if strings.Contains(cfg.Query, "$") { - errs = append(errs, errors.New("stored_requests.postgres.initialize_caches.query should not contain any wildcards (e.g. $1)")) - } - return errs -} - -func (cfg *PostgresCacheInitializer) validate(errs configErrors) configErrors { - if cfg.Query == "" && cfg.AmpQuery == "" { - return errs - } - - slim := &PostgresCacheInitializerSlim{Timeout: cfg.Timeout, Query: cfg.Query} - errs = slim.validate(errs) - - if strings.Contains(cfg.AmpQuery, "$") { - errs = append(errs, errors.New("stored_requests.postgres.initialize_caches.amp_query should not contain any wildcards (e.g. $1)")) + errs = append(errs, fmt.Errorf("%s: postgres.initialize_caches.query should not contain any wildcards (e.g. $1)", section)) } - return errs } -type PostgresUpdatePollingSlim struct { - // RefreshRate determines how frequently the Query and AmpQuery are run. - RefreshRate int `mapstructure:"refresh_rate_seconds"` - - // Timeout is the amount of time before a call to the database is aborted. - Timeout int `mapstructure:"timeout_ms"` - - // An example UpdateQuery is: - // - // SELECT id, requestData, 'request' AS type - // FROM stored_requests - // WHERE last_updated > $1 - // UNION ALL - // SELECT id, requestData, 'imp' AS type - // FROM stored_imps - // WHERE last_updated > $1 - // - // The code will be run periodically to fetch updates from the database. - Query string `mapstructure:"query"` -} - type PostgresUpdatePolling struct { // RefreshRate determines how frequently the Query and AmpQuery are run. RefreshRate int `mapstructure:"refresh_rate_seconds"` @@ -370,49 +285,31 @@ type PostgresUpdatePolling struct { AmpQuery string `mapstructure:"amp_query"` } -func (cfg *PostgresUpdatePollingSlim) validate(errs configErrors) configErrors { +func (cfg *PostgresUpdatePolling) validate(section string, errs configErrors) configErrors { if cfg.Query == "" { return errs } if cfg.RefreshRate <= 0 { - errs = append(errs, errors.New("stored_requests.postgres.poll_for_updates.refresh_rate_seconds must be > 0")) + errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.refresh_rate_seconds must be > 0", section)) } if cfg.Timeout <= 0 { - errs = append(errs, errors.New("stored_requests.postgres.poll_for_updates.timeout_ms must be > 0")) + errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.timeout_ms must be > 0", section)) } if !strings.Contains(cfg.Query, "$1") || strings.Contains(cfg.Query, "$2") { - errs = append(errs, errors.New("stored_requests.postgres.poll_for_updates.query must contain exactly one wildcard")) - } - return errs -} - -func (cfg *PostgresUpdatePolling) validate(errs configErrors) configErrors { - if cfg.Query == "" && cfg.AmpQuery == "" { - return errs - } - slim := &PostgresUpdatePollingSlim{RefreshRate: cfg.RefreshRate, Timeout: cfg.Timeout, Query: cfg.Query} - errs = slim.validate(errs) - - if !strings.Contains(cfg.AmpQuery, "$1") || strings.Contains(cfg.AmpQuery, "$2") { - errs = append(errs, errors.New("stored_requests.postgres.poll_for_updates.amp_query must contain exactly one wildcard")) + errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.query must contain exactly one wildcard", section)) } return errs } // MakeQuery builds a query which can fetch numReqs Stored Requests and numImps Stored Imps. // See the docs on PostgresConfig.QueryTemplate for a description of how it works. -func (cfg *PostgresFetcherQueriesSlim) MakeQuery(numReqs int, numImps int) (query string) { +func (cfg *PostgresFetcherQueries) MakeQuery(numReqs int, numImps int) (query string) { return resolve(cfg.QueryTemplate, numReqs, numImps) } -// MakeAmpQuery is the equivalent of MakeQuery() for AMP. -func (cfg *PostgresFetcherQueries) MakeAmpQuery(numReqs int, numImps int) string { - return resolve(cfg.AmpQueryTemplate, numReqs, numImps) -} - func resolve(template string, numReqs int, numImps int) (query string) { numReqs = ensureNonNegative("Request", numReqs) numImps = ensureNonNegative("Imp", numImps) @@ -473,29 +370,29 @@ type InMemoryCache struct { ImpCacheSize int `mapstructure:"imp_cache_size_bytes"` } -func (cfg *InMemoryCache) validate(errs configErrors) configErrors { +func (cfg *InMemoryCache) validate(section string, errs configErrors) configErrors { switch cfg.Type { case "none": // No errors for no config options case "unbounded": if cfg.TTL != 0 { - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache must be 0 for unbounded caches. Got %d", cfg.TTL)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache must be 0 for unbounded caches. Got %d", section, cfg.TTL)) } if cfg.RequestCacheSize != 0 { - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache.request_cache_size_bytes must be 0 for unbounded caches. Got %d", cfg.RequestCacheSize)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache.request_cache_size_bytes must be 0 for unbounded caches. Got %d", section, cfg.RequestCacheSize)) } if cfg.ImpCacheSize != 0 { - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache.imp_cache_size_bytes must be 0 for unbounded caches. Got %d", cfg.ImpCacheSize)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache.imp_cache_size_bytes must be 0 for unbounded caches. Got %d", section, cfg.ImpCacheSize)) } case "lru": if cfg.RequestCacheSize <= 0 { - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache.request_cache_size_bytes must be >= 0 when stored_requests.in_memory_cache.type=lru. Got %d", cfg.RequestCacheSize)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache.request_cache_size_bytes must be >= 0 when in_memory_cache.type=lru. Got %d", section, cfg.RequestCacheSize)) } if cfg.ImpCacheSize <= 0 { - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache.imp_cache_size_bytes must be >= 0 when stored_requests.in_memory_cache.type=lru. Got %d", cfg.ImpCacheSize)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache.imp_cache_size_bytes must be >= 0 when in_memory_cache.type=lru. Got %d", section, cfg.ImpCacheSize)) } default: - errs = append(errs, fmt.Errorf("stored_requests.in_memory_cache.type %s is invalid", cfg.Type)) + errs = append(errs, fmt.Errorf("%s: in_memory_cache.type %s is invalid", section, cfg.Type)) } return errs } diff --git a/config/stored_requests_test.go b/config/stored_requests_test.go index 3e01dd3c853..36a5e3793ed 100644 --- a/config/stored_requests_test.go +++ b/config/stored_requests_test.go @@ -78,40 +78,40 @@ func TestPostgressConnString(t *testing.T) { func TestInMemoryCacheValidation(t *testing.T) { assertNoErrs(t, (&InMemoryCache{ Type: "unbounded", - }).validate(nil)) + }).validate("Test", nil)) assertNoErrs(t, (&InMemoryCache{ Type: "none", - }).validate(nil)) + }).validate("Test", nil)) assertNoErrs(t, (&InMemoryCache{ Type: "lru", RequestCacheSize: 1000, ImpCacheSize: 1000, - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "unrecognized", - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "unbounded", ImpCacheSize: 1000, - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "unbounded", RequestCacheSize: 1000, - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "unbounded", TTL: 500, - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "lru", RequestCacheSize: 0, ImpCacheSize: 1000, - }).validate(nil)) + }).validate("Test", nil)) assertErrsExist(t, (&InMemoryCache{ Type: "lru", RequestCacheSize: 1000, ImpCacheSize: 0, - }).validate(nil)) + }).validate("Test", nil)) } func assertErrsExist(t *testing.T, err configErrors) { @@ -140,7 +140,7 @@ func assertHasValue(t *testing.T, m map[string]string, key string, val string) { } func buildQuery(template string, numReqs int, numImps int) string { - cfg := PostgresFetcherQueriesSlim{} + cfg := PostgresFetcherQueries{} cfg.QueryTemplate = template return cfg.MakeQuery(numReqs, numImps) @@ -152,3 +152,67 @@ func assertStringsEqual(t *testing.T, actual string, expected string) { } } + +func TestResolveConfig(t *testing.T) { + cfg := &Configuration{ + StoredRequests: StoredRequests{ + Files: FileFetcherConfig{ + Enabled: true, + Path: "/test-path"}, + Postgres: PostgresConfig{ + ConnectionInfo: PostgresConnection{ + Database: "db", + Host: "pghost", + Port: 5, + Username: "user", + Password: "pass", + }, + FetcherQueries: PostgresFetcherQueries{ + AmpQueryTemplate: "amp-fetcher-query", + }, + CacheInitialization: PostgresCacheInitializer{ + AmpQuery: "amp-cache-init-query", + }, + PollUpdates: PostgresUpdatePolling{ + AmpQuery: "amp-poll-query", + }, + }, + HTTP: HTTPFetcherConfig{ + AmpEndpoint: "amp-http-fetcher-endpoint", + }, + InMemoryCache: InMemoryCache{ + Type: "none", + TTL: 50, + RequestCacheSize: 1, + ImpCacheSize: 2, + }, + CacheEvents: CacheEventsConfig{ + Enabled: true, + }, + HTTPEvents: HTTPEventsConfig{ + AmpEndpoint: "amp-http-events-endpoint", + }, + }, + } + + cfg.StoredRequests.Postgres.FetcherQueries.QueryTemplate = "auc-fetcher-query" + cfg.StoredRequests.Postgres.CacheInitialization.Query = "auc-cache-init-query" + cfg.StoredRequests.Postgres.PollUpdates.Query = "auc-poll-query" + cfg.StoredRequests.HTTP.Endpoint = "auc-http-fetcher-endpoint" + cfg.StoredRequests.HTTPEvents.Endpoint = "auc-http-events-endpoint" + + resolvedStoredRequestsConfig(cfg) + auc := &cfg.StoredRequests + amp := &cfg.StoredRequestsAMP + + // Auction should have the non-amp values in it + assertStringsEqual(t, auc.CacheEvents.Endpoint, "/storedrequests/openrtb2") + + // Amp should have the amp values in it + assertStringsEqual(t, amp.Postgres.FetcherQueries.QueryTemplate, cfg.StoredRequests.Postgres.FetcherQueries.AmpQueryTemplate) + assertStringsEqual(t, amp.Postgres.CacheInitialization.Query, cfg.StoredRequests.Postgres.CacheInitialization.AmpQuery) + assertStringsEqual(t, amp.Postgres.PollUpdates.Query, cfg.StoredRequests.Postgres.PollUpdates.AmpQuery) + assertStringsEqual(t, amp.HTTP.Endpoint, cfg.StoredRequests.HTTP.AmpEndpoint) + assertStringsEqual(t, amp.HTTPEvents.Endpoint, cfg.StoredRequests.HTTPEvents.AmpEndpoint) + assertStringsEqual(t, amp.CacheEvents.Endpoint, "/storedrequests/amp") +} diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go index e8f6b0bdf46..8f06efcb32b 100644 --- a/stored_requests/config/config.go +++ b/stored_requests/config/config.go @@ -41,25 +41,26 @@ type dbConnection struct { // // As a side-effect, it will add some endpoints to the router if the config calls for it. // In the future we should look for ways to simplify this so that it's not doing two things. -func CreateStoredRequests(cfg *config.StoredRequestsSlim, metricsEngine pbsmetrics.MetricsEngine, client *http.Client, router *httprouter.Router, dbc *dbConnection) (fetcher stored_requests.AllFetcher, shutdown func()) { +func CreateStoredRequests(cfg *config.StoredRequests, metricsEngine pbsmetrics.MetricsEngine, client *http.Client, router *httprouter.Router, dbc *dbConnection) (fetcher stored_requests.AllFetcher, shutdown func()) { // Create database connection if given options for one if cfg.Postgres.ConnectionInfo.Database != "" { conn := cfg.Postgres.ConnectionInfo.ConnString() if dbc.conn == "" { - glog.Infof("Connecting to Postgres for Stored Requests. DB=%s, host=%s, port=%d, user=%s", + glog.Infof("Connecting to Postgres for Stored %s. DB=%s, host=%s, port=%d, user=%s", + cfg.DataType(), cfg.Postgres.ConnectionInfo.Database, cfg.Postgres.ConnectionInfo.Host, cfg.Postgres.ConnectionInfo.Port, cfg.Postgres.ConnectionInfo.Username) - db := newPostgresDB(cfg.Postgres.ConnectionInfo) + db := newPostgresDB(cfg.DataType(), cfg.Postgres.ConnectionInfo) dbc.conn = conn dbc.db = db } // Error out if config is trying to use multiple database connections for different stored requests (not supported yet) if conn != dbc.conn { - glog.Fatal("Multiple database connection settings found in Stored Requests config, only a single database connection is currently supported.") + glog.Fatal("Multiple database connection settings found in config, only a single database connection is currently supported.") } } @@ -106,9 +107,6 @@ func CreateStoredRequests(cfg *config.StoredRequestsSlim, metricsEngine pbsmetri // As a side-effect, it will add some endpoints to the router if the config calls for it. // In the future we should look for ways to simplify this so that it's not doing two things. func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, client *http.Client, router *httprouter.Router) (db *sql.DB, shutdown func(), fetcher stored_requests.Fetcher, ampFetcher stored_requests.Fetcher, categoriesFetcher stored_requests.CategoryFetcher, videoFetcher stored_requests.Fetcher) { - // Build individual slim options from combined config struct - slimAuction, slimAmp := resolvedStoredRequestsConfig(cfg) - // TODO: Switch this to be set in config defaults //if cfg.CategoryMapping.CacheEvents.Enabled && cfg.CategoryMapping.CacheEvents.Endpoint == "" { // cfg.CategoryMapping.CacheEvents.Endpoint = "/storedrequest/categorymapping" @@ -116,8 +114,8 @@ func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.Metri var dbc dbConnection - fetcher1, shutdown1 := CreateStoredRequests(&slimAuction, metricsEngine, client, router, &dbc) - fetcher2, shutdown2 := CreateStoredRequests(&slimAmp, metricsEngine, client, router, &dbc) + fetcher1, shutdown1 := CreateStoredRequests(&cfg.StoredRequests, metricsEngine, client, router, &dbc) + fetcher2, shutdown2 := CreateStoredRequests(&cfg.StoredRequestsAMP, metricsEngine, client, router, &dbc) fetcher3, shutdown3 := CreateStoredRequests(&cfg.CategoryMapping, metricsEngine, client, router, &dbc) fetcher4, shutdown4 := CreateStoredRequests(&cfg.StoredVideo, metricsEngine, client, router, &dbc) @@ -138,48 +136,6 @@ func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.Metri return } -func resolvedStoredRequestsConfig(cfg *config.Configuration) (auc, amp config.StoredRequestsSlim) { - sr := &cfg.StoredRequests - - // Auction endpoint uses non-Amp fields so can just copy the slin data - auc.Files.Enabled = sr.Files - auc.Files.Path = sr.Path - auc.Postgres.ConnectionInfo = sr.Postgres.ConnectionInfo - auc.Postgres.FetcherQueries.QueryTemplate = sr.Postgres.FetcherQueries.QueryTemplate - auc.Postgres.CacheInitialization.Timeout = sr.Postgres.CacheInitialization.Timeout - auc.Postgres.CacheInitialization.Query = sr.Postgres.CacheInitialization.Query - auc.Postgres.PollUpdates.RefreshRate = sr.Postgres.PollUpdates.RefreshRate - auc.Postgres.PollUpdates.Timeout = sr.Postgres.PollUpdates.Timeout - auc.Postgres.PollUpdates.Query = sr.Postgres.PollUpdates.Query - auc.HTTP.Endpoint = sr.HTTP.Endpoint - auc.InMemoryCache = sr.InMemoryCache - auc.CacheEvents.Enabled = sr.CacheEventsAPI - auc.CacheEvents.Endpoint = "/storedrequests/openrtb2" - auc.HTTPEvents.RefreshRate = sr.HTTPEvents.RefreshRate - auc.HTTPEvents.Timeout = sr.HTTPEvents.Timeout - auc.HTTPEvents.Endpoint = sr.HTTPEvents.Endpoint - - // Amp endpoint uses all the slim data but some fields get replacyed by Amp* version of similar fields - amp.Files.Enabled = sr.Files - amp.Files.Path = sr.Path - amp.Postgres.ConnectionInfo = sr.Postgres.ConnectionInfo - amp.Postgres.FetcherQueries.QueryTemplate = sr.Postgres.FetcherQueries.AmpQueryTemplate - amp.Postgres.CacheInitialization.Timeout = sr.Postgres.CacheInitialization.Timeout - amp.Postgres.CacheInitialization.Query = sr.Postgres.CacheInitialization.AmpQuery - amp.Postgres.PollUpdates.RefreshRate = sr.Postgres.PollUpdates.RefreshRate - amp.Postgres.PollUpdates.Timeout = sr.Postgres.PollUpdates.Timeout - amp.Postgres.PollUpdates.Query = sr.Postgres.PollUpdates.AmpQuery - amp.HTTP.Endpoint = sr.HTTP.AmpEndpoint - amp.InMemoryCache = sr.InMemoryCache - amp.CacheEvents.Enabled = sr.CacheEventsAPI - amp.CacheEvents.Endpoint = "/storedrequests/amp" - amp.HTTPEvents.RefreshRate = sr.HTTPEvents.RefreshRate - amp.HTTPEvents.Timeout = sr.HTTPEvents.Timeout - amp.HTTPEvents.Endpoint = sr.HTTPEvents.AmpEndpoint - - return -} - func addListeners(cache stored_requests.Cache, eventProducers []events.EventProducer) (shutdown func()) { listeners := make([]*events.EventListener, 0, len(eventProducers)) @@ -196,36 +152,36 @@ func addListeners(cache stored_requests.Cache, eventProducers []events.EventProd } } -func newFetcher(cfg *config.StoredRequestsSlim, client *http.Client, db *sql.DB) (fetcher stored_requests.AllFetcher) { +func newFetcher(cfg *config.StoredRequests, client *http.Client, db *sql.DB) (fetcher stored_requests.AllFetcher) { idList := make(stored_requests.MultiFetcher, 0, 3) if cfg.Files.Enabled { - fFetcher := newFilesystem(cfg.Files.Path) + fFetcher := newFilesystem(cfg.DataType(), cfg.Files.Path) idList = append(idList, fFetcher) } if cfg.Postgres.FetcherQueries.QueryTemplate != "" { - glog.Infof("Loading Stored Requests via Postgres.\nQuery: %s", cfg.Postgres.FetcherQueries.QueryTemplate) + glog.Infof("Loading Stored %s data via Postgres.\nQuery: %s", cfg.DataType(), cfg.Postgres.FetcherQueries.QueryTemplate) idList = append(idList, db_fetcher.NewFetcher(db, cfg.Postgres.FetcherQueries.MakeQuery)) } if cfg.HTTP.Endpoint != "" { - glog.Infof("Loading Stored Requests via HTTP. endpoint=%s", cfg.HTTP.Endpoint) + glog.Infof("Loading Stored %s data via HTTP. endpoint=%s", cfg.DataType(), cfg.HTTP.Endpoint) idList = append(idList, http_fetcher.NewFetcher(client, cfg.HTTP.Endpoint)) } - fetcher = consolidate(idList) + fetcher = consolidate(cfg.DataType(), idList) return } -func newCache(cfg *config.StoredRequestsSlim) stored_requests.Cache { +func newCache(cfg *config.StoredRequests) stored_requests.Cache { if cfg.InMemoryCache.Type == "none" { - glog.Info("No Stored Request cache configured. The Fetcher backend will be used for all Stored Requests.") + glog.Infof("No Stored %s cache configured. The %s Fetcher backend will be used for all data requests", cfg.DataType(), cfg.DataType()) return &nil_cache.NilCache{} } return memory.NewCache(&cfg.InMemoryCache) } -func newEventProducers(cfg *config.StoredRequestsSlim, client *http.Client, db *sql.DB, router *httprouter.Router) (eventProducers []events.EventProducer) { +func newEventProducers(cfg *config.StoredRequests, client *http.Client, db *sql.DB, router *httprouter.Router) (eventProducers []events.EventProducer) { if cfg.CacheEvents.Enabled { eventProducers = append(eventProducers, newEventsAPI(router, cfg.CacheEvents.Endpoint)) } @@ -247,7 +203,7 @@ func newEventProducers(cfg *config.StoredRequestsSlim, client *http.Client, db * return } -func newPostgresPolling(cfg config.PostgresUpdatePollingSlim, db *sql.DB, startTime time.Time) events.EventProducer { +func newPostgresPolling(cfg config.PostgresUpdatePolling, db *sql.DB, startTime time.Time) events.EventProducer { timeout := time.Duration(cfg.Timeout) * time.Millisecond ctxProducer := func() (ctx context.Context, canceller func()) { return context.WithTimeout(context.Background(), timeout) @@ -269,32 +225,37 @@ func newHttpEvents(client *http.Client, timeout time.Duration, refreshRate time. return httpEvents.NewHTTPEvents(client, endpoint, ctxProducer, refreshRate) } -func newFilesystem(configPath string) stored_requests.AllFetcher { - glog.Infof("Loading Stored Requests from filesystem at path %s", configPath) +func newFilesystem(dataType config.DataType, configPath string) stored_requests.AllFetcher { + glog.Infof("Loading Stored %s data from filesystem at path %s", dataType, configPath) fetcher, err := file_fetcher.NewFileFetcher(configPath) if err != nil { - glog.Fatalf("Failed to create a FileFetcher: %v", err) + glog.Fatalf("Failed to create a %s FileFetcher: %v", dataType, err) } return fetcher } -func newPostgresDB(cfg config.PostgresConnection) *sql.DB { +func newPostgresDB(dataType config.DataType, cfg config.PostgresConnection) *sql.DB { db, err := sql.Open("postgres", cfg.ConnString()) if err != nil { - glog.Fatalf("Failed to open postgres connection: %v", err) + glog.Fatalf("Failed to open %s postgres connection: %v", dataType, err) } if err := db.Ping(); err != nil { - glog.Fatalf("Failed to ping postgres: %v", err) + glog.Fatalf("Failed to ping %s postgres: %v", dataType, err) } return db } // consolidate returns a single Fetcher from an array of fetchers of any size. -func consolidate(fetchers []stored_requests.AllFetcher) stored_requests.AllFetcher { +func consolidate(dataType config.DataType, fetchers []stored_requests.AllFetcher) stored_requests.AllFetcher { if len(fetchers) == 0 { - glog.Warning("No Stored Request support configured. request.imp[i].ext.prebid.storedrequest will be ignored. If you need this, check your app config") + switch dataType { + case config.RequestDataType: + glog.Warning("No Stored Request support configured. request.imp[i].ext.prebid.storedrequest will be ignored. If you need this, check your app config") + default: + glog.Warningf("No Stored %s support configured. If you need this, check your app config", dataType) + } return empty_fetcher.EmptyFetcher{} } else if len(fetchers) == 1 { return fetchers[0] diff --git a/stored_requests/config/config_test.go b/stored_requests/config/config_test.go index 11748a59966..4c3943ea5be 100644 --- a/stored_requests/config/config_test.go +++ b/stored_requests/config/config_test.go @@ -19,8 +19,8 @@ import ( ) func TestNewEmptyFetcher(t *testing.T) { - fetcher := newFetcher(&config.StoredRequestsSlim{}, nil, nil) - ampFetcher := newFetcher(&config.StoredRequestsSlim{}, nil, nil) + fetcher := newFetcher(&config.StoredRequests{}, nil, nil) + ampFetcher := newFetcher(&config.StoredRequests{}, nil, nil) if fetcher == nil || ampFetcher == nil { t.Errorf("The fetchers should be non-nil, even with an empty config.") } @@ -33,13 +33,13 @@ func TestNewEmptyFetcher(t *testing.T) { } func TestNewHTTPFetcher(t *testing.T) { - fetcher := newFetcher(&config.StoredRequestsSlim{ - HTTP: config.HTTPFetcherConfigSlim{ + fetcher := newFetcher(&config.StoredRequests{ + HTTP: config.HTTPFetcherConfig{ Endpoint: "stored-requests.prebid.com", }, }, nil, nil) - ampFetcher := newFetcher(&config.StoredRequestsSlim{ - HTTP: config.HTTPFetcherConfigSlim{ + ampFetcher := newFetcher(&config.StoredRequests{ + HTTP: config.HTTPFetcherConfig{ Endpoint: "stored-requests.prebid.com?type=amp", }, }, nil, nil) @@ -60,13 +60,13 @@ func TestNewHTTPFetcher(t *testing.T) { } func TestNewHTTPFetcherNoAmp(t *testing.T) { - fetcher := newFetcher(&config.StoredRequestsSlim{ - HTTP: config.HTTPFetcherConfigSlim{ + fetcher := newFetcher(&config.StoredRequests{ + HTTP: config.HTTPFetcherConfig{ Endpoint: "stored-requests.prebid.com", }, }, nil, nil) - ampFetcher := newFetcher(&config.StoredRequestsSlim{ - HTTP: config.HTTPFetcherConfigSlim{ + ampFetcher := newFetcher(&config.StoredRequests{ + HTTP: config.HTTPFetcherConfig{ Endpoint: "", }, }, nil, nil) @@ -82,78 +82,14 @@ func TestNewHTTPFetcherNoAmp(t *testing.T) { } } -func TestResolveConfig(t *testing.T) { - cfg := &config.Configuration{ - StoredRequests: config.StoredRequests{ - Files: true, - Path: "/test-path", - Postgres: config.PostgresConfig{ - ConnectionInfo: config.PostgresConnection{ - Database: "db", - Host: "pghost", - Port: 5, - Username: "user", - Password: "pass", - }, - FetcherQueries: config.PostgresFetcherQueries{ - AmpQueryTemplate: "amp-fetcher-query", - }, - CacheInitialization: config.PostgresCacheInitializer{ - AmpQuery: "amp-cache-init-query", - }, - PollUpdates: config.PostgresUpdatePolling{ - AmpQuery: "amp-poll-query", - }, - }, - HTTP: config.HTTPFetcherConfig{ - AmpEndpoint: "amp-http-fetcher-endpoint", - }, - InMemoryCache: config.InMemoryCache{ - Type: "none", - TTL: 50, - RequestCacheSize: 1, - ImpCacheSize: 2, - }, - CacheEventsAPI: true, - HTTPEvents: config.HTTPEventsConfig{ - AmpEndpoint: "amp-http-events-endpoint", - }, - }, - } - - cfg.StoredRequests.Postgres.FetcherQueries.QueryTemplate = "auc-fetcher-query" - cfg.StoredRequests.Postgres.CacheInitialization.Query = "auc-cache-init-query" - cfg.StoredRequests.Postgres.PollUpdates.Query = "auc-poll-query" - cfg.StoredRequests.HTTP.Endpoint = "auc-http-fetcher-endpoint" - cfg.StoredRequests.HTTPEvents.Endpoint = "auc-http-events-endpoint" - - auc, amp := resolvedStoredRequestsConfig(cfg) - - // Auction slim should have the non-amp values in it - assertStringsEqual(t, auc.Postgres.FetcherQueries.QueryTemplate, cfg.StoredRequests.Postgres.FetcherQueries.QueryTemplate) - assertStringsEqual(t, auc.Postgres.CacheInitialization.Query, cfg.StoredRequests.Postgres.CacheInitialization.Query) - assertStringsEqual(t, auc.Postgres.PollUpdates.Query, cfg.StoredRequests.Postgres.PollUpdates.Query) - assertStringsEqual(t, auc.HTTP.Endpoint, cfg.StoredRequests.HTTP.Endpoint) - assertStringsEqual(t, auc.HTTPEvents.Endpoint, cfg.StoredRequests.HTTPEvents.Endpoint) - assertStringsEqual(t, auc.CacheEvents.Endpoint, "/storedrequests/openrtb2") - - // Amp slim should have the amp values in it - assertStringsEqual(t, amp.Postgres.FetcherQueries.QueryTemplate, cfg.StoredRequests.Postgres.FetcherQueries.AmpQueryTemplate) - assertStringsEqual(t, amp.Postgres.CacheInitialization.Query, cfg.StoredRequests.Postgres.CacheInitialization.AmpQuery) - assertStringsEqual(t, amp.Postgres.PollUpdates.Query, cfg.StoredRequests.Postgres.PollUpdates.AmpQuery) - assertStringsEqual(t, amp.HTTP.Endpoint, cfg.StoredRequests.HTTP.AmpEndpoint) - assertStringsEqual(t, amp.HTTPEvents.Endpoint, cfg.StoredRequests.HTTPEvents.AmpEndpoint) - assertStringsEqual(t, amp.CacheEvents.Endpoint, "/storedrequests/amp") -} - func TestNewHTTPEvents(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) } server1 := httptest.NewServer(http.HandlerFunc(handler)) - cfg := &config.StoredRequestsSlim{ - HTTPEvents: config.HTTPEventsConfigSlim{ + cfg := &config.StoredRequests{ + HTTPEvents: config.HTTPEventsConfig{ Endpoint: server1.URL, RefreshRate: 100, Timeout: 1000, @@ -165,7 +101,7 @@ func TestNewHTTPEvents(t *testing.T) { } func TestNewEmptyCache(t *testing.T) { - cache := newCache(&config.StoredRequestsSlim{InMemoryCache: config.InMemoryCache{Type: "none"}}) + cache := newCache(&config.StoredRequests{InMemoryCache: config.InMemoryCache{Type: "none"}}) cache.Save(context.Background(), map[string]json.RawMessage{"foo": json.RawMessage("true")}, nil) reqs, _ := cache.Get(context.Background(), []string{"foo"}, nil) if len(reqs) != 0 { @@ -174,7 +110,7 @@ func TestNewEmptyCache(t *testing.T) { } func TestNewInMemoryCache(t *testing.T) { - cache := newCache(&config.StoredRequestsSlim{ + cache := newCache(&config.StoredRequests{ InMemoryCache: config.InMemoryCache{ TTL: 60, RequestCacheSize: 100, @@ -189,26 +125,26 @@ func TestNewInMemoryCache(t *testing.T) { } func TestNewPostgresEventProducers(t *testing.T) { - cfg := &config.StoredRequestsSlim{ - Postgres: config.PostgresConfigSlim{ - CacheInitialization: config.PostgresCacheInitializerSlim{ + cfg := &config.StoredRequests{ + Postgres: config.PostgresConfig{ + CacheInitialization: config.PostgresCacheInitializer{ Timeout: 50, Query: "SELECT id, requestData, type FROM stored_data", }, - PollUpdates: config.PostgresUpdatePollingSlim{ + PollUpdates: config.PostgresUpdatePolling{ RefreshRate: 20, Timeout: 50, Query: "SELECT id, requestData, type FROM stored_data WHERE last_updated > $1", }, }, } - ampCfg := &config.StoredRequestsSlim{ - Postgres: config.PostgresConfigSlim{ - CacheInitialization: config.PostgresCacheInitializerSlim{ + ampCfg := &config.StoredRequests{ + Postgres: config.PostgresConfig{ + CacheInitialization: config.PostgresCacheInitializer{ Timeout: 50, Query: "SELECT id, requestData, type FROM stored_amp_data", }, - PollUpdates: config.PostgresUpdatePollingSlim{ + PollUpdates: config.PostgresUpdatePolling{ RefreshRate: 20, Timeout: 50, Query: "SELECT id, requestData, type FROM stored_amp_data WHERE last_updated > $1", From f350cdab662a6feca661226fec0cd10f69363ab8 Mon Sep 17 00:00:00 2001 From: guscarreon Date: Wed, 2 Sep 2020 12:51:09 -0400 Subject: [PATCH 187/318] Fix Test TestEventChannel_OutputFormat (#1468) --- analytics/pubstack/eventchannel/eventchannel_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/analytics/pubstack/eventchannel/eventchannel_test.go b/analytics/pubstack/eventchannel/eventchannel_test.go index 792e15e151e..f450fb61fe1 100644 --- a/analytics/pubstack/eventchannel/eventchannel_test.go +++ b/analytics/pubstack/eventchannel/eventchannel_test.go @@ -3,11 +3,12 @@ package eventchannel import ( "bytes" "compress/gzip" - "github.com/stretchr/testify/assert" "io/ioutil" "sync" "testing" "time" + + "github.com/stretchr/testify/assert" ) var maxByteSize = int64(15) @@ -160,16 +161,21 @@ func TestEventChannel_OutputFormat(t *testing.T) { eventChannel := NewEventChannel(send, 15000, 10, 2*time.Minute) eventChannel.Push([]byte("one")) + time.Sleep(1 * time.Millisecond) + eventChannel.flush() + eventChannel.Push([]byte("two")) + time.Sleep(1 * time.Millisecond) + eventChannel.Push([]byte("three")) + time.Sleep(1 * time.Millisecond) eventChannel.Close() - time.Sleep(10 * time.Millisecond) + time.Sleep(1 * time.Millisecond) expected := append(toGzip("one"), toGzip("twothree")...) assert.Equal(t, expected, data) - } From c867e6f7edbd8fbe197b16441f1792a05642973b Mon Sep 17 00:00:00 2001 From: Mansi Nahar Date: Wed, 2 Sep 2020 16:24:49 -0400 Subject: [PATCH 188/318] Add ability to randomly generate source.TID if empty and set publisher.ID to resolved account ID (#1439) --- config/config.go | 3 + config/config_test.go | 3 + endpoints/openrtb2/amp_auction.go | 35 ++++-- endpoints/openrtb2/amp_auction_test.go | 79 +++++++++++++ endpoints/openrtb2/auction.go | 31 ++++- endpoints/openrtb2/auction_test.go | 158 +++++++++++++++++++++++-- endpoints/openrtb2/video_auction.go | 4 +- exchange/utils.go | 7 +- 8 files changed, 293 insertions(+), 27 deletions(-) diff --git a/config/config.go b/config/config.go index e443a48ec1d..23cc35719db 100755 --- a/config/config.go +++ b/config/config.go @@ -73,6 +73,8 @@ type Configuration struct { Debug Debug `mapstructure:"debug"` // RequestValidation specifies the request validation options. RequestValidation RequestValidation `mapstructure:"request_validation"` + // When true, PBS will assign a randomly generated UUID to req.Source.TID if it is empty + AutoGenSourceTID bool `mapstructure:"auto_gen_source_tid"` } const MIN_COOKIE_SIZE_BYTES = 500 @@ -975,6 +977,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("blacklisted_accts", []string{""}) v.SetDefault("account_required", false) v.SetDefault("certificates_file", "") + v.SetDefault("auto_gen_source_tid", true) v.SetDefault("request_timeout_headers.request_time_in_queue", "") v.SetDefault("request_timeout_headers.request_timeout_in_queue", "") diff --git a/config/config_test.go b/config/config_test.go index b23ddd6f614..23764d216ef 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -138,6 +138,7 @@ func TestDefaults(t *testing.T) { cmpStrings(t, "certificates_file", cfg.PemCertsFile, "") cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled) cmpStrings(t, "stored_requests.filesystem.directorypath", "./stored_requests/data/by_id", cfg.StoredRequests.Files.Path) + cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, true) } var fullConfig = []byte(` @@ -224,6 +225,7 @@ adapters: usersync_url: https://tag.adkernel.com/syncr?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r= blacklisted_apps: ["spamAppID","sketchy-app-id"] account_required: true +auto_gen_source_tid: false certificates_file: /etc/ssl/cert.pem request_validation: ipv4_private_networks: ["1.1.1.0/24"] @@ -406,6 +408,7 @@ func TestFullConfig(t *testing.T) { cmpStrings(t, "adapters.rhythmone.endpoint", cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint, "http://tag.1rx.io/rmp") cmpStrings(t, "adapters.rhythmone.usersync_url", cfg.Adapters[string(openrtb_ext.BidderRhythmone)].UserSyncURL, "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Fprebid-server.prebid.org%2F%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BRX_UUID%5D") cmpBools(t, "account_required", cfg.AccountRequired, true) + cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, false) cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true) cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true) cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem") diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 8efba5a926c..f5b72e029e0 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -154,7 +154,8 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h } else { labels.CookieFlag = pbsmetrics.CookieFlagYes } - labels.PubID = effectivePubID(req.Site.Publisher) + labels.PubID = getAccountID(req.Site.Publisher) + // Blacklist account now that we have resolved the value if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { errL = append(errL, acctIdErr) @@ -388,13 +389,7 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope setAmpExt(req.Site, "1") - account := httpRequest.FormValue("account") - if account != "" { - if req.Site.Publisher == nil { - req.Site.Publisher = &openrtb.Publisher{} - } - req.Site.Publisher.ID = account - } + setEffectiveAmpPubID(req, httpRequest.URL.Query()) slot := httpRequest.FormValue("slot") if slot != "" { @@ -564,3 +559,27 @@ func readConsent(url *url.URL) string { // Fallback to 'gdpr_consent' for compatability until it's no longer used by AMP. return url.Query().Get("gdpr_consent") } + +// Sets the effective publisher ID for amp request +func setEffectiveAmpPubID(req *openrtb.BidRequest, urlQueryParams url.Values) { + var pub *openrtb.Publisher + if req.App != nil { + if req.App.Publisher == nil { + req.App.Publisher = new(openrtb.Publisher) + } + pub = req.App.Publisher + } else if req.Site != nil { + if req.Site.Publisher == nil { + req.Site.Publisher = new(openrtb.Publisher) + } + pub = req.Site.Publisher + } + + if pub.ID == "" { + // For amp requests, the publisher ID could be sent via the account + // query string + if acc := urlQueryParams.Get("account"); acc != "" && acc != "ACCOUNT_ID" { + pub.ID = acc + } + } +} diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 692d3fb0c5d..be6735c4c39 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -1042,3 +1042,82 @@ func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, return json.Marshal(bidRequest) } + +func TestSetEffectiveAmpPubID(t *testing.T) { + testPubID := "test-pub" + testURLQueryParams := url.Values{} + testURLQueryParams.Add("account", testPubID) + + testCases := []struct { + description string + req *openrtb.BidRequest + urlQueryParams url.Values + expectedPubID string + }{ + { + description: "No publisher ID provided", + req: &openrtb.BidRequest{ + App: &openrtb.App{ + Publisher: nil, + }, + }, + expectedPubID: "", + }, + { + description: "Publisher ID present in req.App.Publisher.ID", + req: &openrtb.BidRequest{ + App: &openrtb.App{ + Publisher: &openrtb.Publisher{ + ID: testPubID, + }, + }, + }, + expectedPubID: testPubID, + }, + { + description: "Publisher ID present in req.Site.Publisher.ID", + req: &openrtb.BidRequest{ + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: testPubID, + }, + }, + }, + expectedPubID: testPubID, + }, + { + description: "Publisher ID present in account query parameter", + req: &openrtb.BidRequest{ + App: &openrtb.App{ + Publisher: &openrtb.Publisher{ + ID: "", + }, + }, + }, + urlQueryParams: testURLQueryParams, + expectedPubID: testPubID, + }, + { + description: "req.Site.Publisher present but ID set to empty string", + req: &openrtb.BidRequest{ + Site: &openrtb.Site{ + Publisher: &openrtb.Publisher{ + ID: "", + }, + }, + }, + expectedPubID: "", + }, + } + + for _, test := range testCases { + setEffectiveAmpPubID(test.req, test.urlQueryParams) + if test.req.Site != nil { + assert.Equal(t, test.expectedPubID, test.req.Site.Publisher.ID, + "should return the expected Publisher ID for test case: %s", test.description) + } else { + assert.Equal(t, test.expectedPubID, test.req.App.Publisher.ID, + "should return the expected Publisher ID for test case: %s", test.description) + } + } +} diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 86186fa8373..bad2f8aae5b 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -15,6 +15,7 @@ import ( "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" + "github.com/gofrs/uuid" "github.com/golang/glog" "github.com/julienschmidt/httprouter" "github.com/mssola/user_agent" @@ -141,7 +142,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http if req.App != nil { labels.Source = pbsmetrics.DemandApp labels.RType = pbsmetrics.ReqTypeORTB2App - labels.PubID = effectivePubID(req.App.Publisher) + labels.PubID = getAccountID(req.App.Publisher) } else { //req.Site != nil labels.Source = pbsmetrics.DemandWeb if usersyncs.LiveSyncCount() == 0 { @@ -149,7 +150,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http } else { labels.CookieFlag = pbsmetrics.CookieFlagYes } - labels.PubID = effectivePubID(req.Site.Publisher) + labels.PubID = getAccountID(req.Site.Publisher) } if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { @@ -283,6 +284,14 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { errL = append(errL, &errortypes.Warning{Message: fmt.Sprintf("A prebid request can only process one currency. Taking the first currency in the list, %s, as the active currency", req.Cur[0])}) } + // If automatically filling source TID is enabled then validate that + // source.TID exists and If it doesn't, fill it with a randomly generated UUID + if deps.cfg.AutoGenSourceTID { + if err := validateAndFillSourceTID(req); err != nil { + return []error{err} + } + } + var aliases map[string]string if bidExt, err := deps.parseBidExt(req.Ext); err != nil { return []error{err} @@ -358,6 +367,20 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { return errL } +func validateAndFillSourceTID(req *openrtb.BidRequest) error { + if req.Source == nil { + req.Source = &openrtb.Source{} + } + if req.Source.TID == "" { + if rawUUID, err := uuid.NewV4(); err == nil { + req.Source.TID = rawUUID.String() + } else { + return errors.New("req.Source.TID missing in the req and error creating a random UID") + } + } + return nil +} + func validateBidAdjustmentFactors(adjustmentFactors map[string]float64, aliases map[string]string) error { for bidderToAdjust, adjustmentFactor := range adjustmentFactors { if adjustmentFactor <= 0 { @@ -1265,8 +1288,8 @@ func writeError(errs []error, w http.ResponseWriter, labels *pbsmetrics.Labels) return rc } -// Returns the effective publisher ID -func effectivePubID(pub *openrtb.Publisher) string { +// Returns the account ID for the request +func getAccountID(pub *openrtb.Publisher) string { if pub != nil { if pub.Ext != nil { var pubExt openrtb_ext.ExtPublisher diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 957760c61c9..6baab8d500f 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1112,16 +1112,6 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { assert.Equal(t, []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, errs) } -func TestEffectivePubID(t *testing.T) { - var pub openrtb.Publisher - assert.Equal(t, pbsmetrics.PublisherUnknown, effectivePubID(nil), "effectivePubID failed for nil Publisher.") - assert.Equal(t, pbsmetrics.PublisherUnknown, effectivePubID(&pub), "effectivePubID failed for empty Publisher.") - pub.ID = "123" - assert.Equal(t, "123", effectivePubID(&pub), "effectivePubID failed for standard Publisher.") - pub.Ext = json.RawMessage(`{"prebid": {"parentAccount": "abc"} }`) - assert.Equal(t, "abc", effectivePubID(&pub), "effectivePubID failed for parentAccount.") -} - func validRequest(t *testing.T, filename string) string { requestData, err := ioutil.ReadFile("sample-requests/valid-whole/supplementary/" + filename) if err != nil { @@ -1222,6 +1212,54 @@ func TestCCPAInvalid(t *testing.T) { assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request") } +func TestValidateSourceTID(t *testing.T) { + cfg := &config.Configuration{ + AutoGenSourceTID: true, + } + + deps := &endpointDeps{ + &nobidExchange{}, + newParamsValidator(t), + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, + cfg, + pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BidderMap, + nil, + nil, + hardcodedResponseIPValidator{response: true}, + } + + ui := uint64(1) + req := openrtb.BidRequest{ + ID: "someID", + Imp: []openrtb.Imp{ + { + ID: "imp-ID", + Banner: &openrtb.Banner{ + W: &ui, + H: &ui, + }, + Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`), + }, + }, + Site: &openrtb.Site{ + ID: "myID", + }, + Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"invalid by length"}`), + }, + } + + deps.validateRequest(&req) + assert.NotEmpty(t, req.Source.TID, "Expected req.Source.TID to be filled with a randomly generated UID") +} + func TestSChainInvalid(t *testing.T) { deps := &endpointDeps{ &nobidExchange{}, @@ -1269,6 +1307,63 @@ func TestSChainInvalid(t *testing.T) { assert.ElementsMatch(t, errL, []error{expectedError}) } +func TestGetAccountID(t *testing.T) { + testPubID := "test-pub" + testParentAccount := "test-account" + testPubExt := openrtb_ext.ExtPublisher{ + Prebid: &openrtb_ext.ExtPublisherPrebid{ + ParentAccount: &testParentAccount, + }, + } + testPubExtJSON, err := json.Marshal(testPubExt) + assert.NoError(t, err) + + testCases := []struct { + description string + pub *openrtb.Publisher + expectedAccID string + }{ + { + description: "Publisher.ID and Publisher.Ext.Prebid.ParentAccount both present", + pub: &openrtb.Publisher{ + ID: testPubID, + Ext: testPubExtJSON, + }, + expectedAccID: testParentAccount, + }, + { + description: "Only Publisher.Ext.Prebid.ParentAccount present", + pub: &openrtb.Publisher{ + ID: "", + Ext: testPubExtJSON, + }, + expectedAccID: testParentAccount, + }, + { + description: "Only Publisher.ID present", + pub: &openrtb.Publisher{ + ID: testPubID, + }, + expectedAccID: testPubID, + }, + { + description: "Neither Publisher.ID or Publisher.Ext.Prebid.ParentAccount present", + pub: &openrtb.Publisher{}, + expectedAccID: pbsmetrics.PublisherUnknown, + }, + { + description: "Publisher is nil", + pub: nil, + expectedAccID: pbsmetrics.PublisherUnknown, + }, + } + + for _, test := range testCases { + acc := getAccountID(test.pub) + assert.Equal(t, test.expectedAccID, acc, "getAccountID should return expected account for test case: %s", test.description) + } +} + func TestSanitizeRequest(t *testing.T) { testCases := []struct { description string @@ -1344,6 +1439,49 @@ func TestSanitizeRequest(t *testing.T) { } } +func TestValidateAndFillSourceTID(t *testing.T) { + testTID := "some-tid" + testCases := []struct { + description string + req *openrtb.BidRequest + expectRandTID bool + expectedTID string + }{ + { + description: "req.Source not present. Expecting a randomly generated TID value", + req: &openrtb.BidRequest{}, + expectRandTID: true, + }, + { + description: "req.Source.TID not present. Expecting a randomly generated TID value", + req: &openrtb.BidRequest{ + Source: &openrtb.Source{}, + }, + expectRandTID: true, + }, + { + description: "req.Source.TID present. Expecting no change", + req: &openrtb.BidRequest{ + Source: &openrtb.Source{ + TID: testTID, + }, + }, + expectRandTID: false, + expectedTID: testTID, + }, + } + + for _, test := range testCases { + _ = validateAndFillSourceTID(test.req) + if test.expectRandTID { + assert.NotEmpty(t, test.req.Source.TID, test.description) + assert.NotEqual(t, test.expectedTID, test.req.Source.TID, test.description) + } else { + assert.Equal(t, test.expectedTID, test.req.Source.TID, test.description) + } + } +} + // nobidExchange is a well-behaved exchange which always bids "no bid". type nobidExchange struct { gotRequest *openrtb.BidRequest diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index a6ca527874a..a8e4c28b167 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -242,7 +242,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re usersyncs := usersync.ParsePBSCookieFromRequest(r, &(deps.cfg.HostCookie)) if bidReq.App != nil { labels.Source = pbsmetrics.DemandApp - labels.PubID = effectivePubID(bidReq.App.Publisher) + labels.PubID = getAccountID(bidReq.App.Publisher) } else { // both bidReq.App == nil and bidReq.Site != nil are true labels.Source = pbsmetrics.DemandWeb if usersyncs.LiveSyncCount() == 0 { @@ -250,7 +250,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } else { labels.CookieFlag = pbsmetrics.CookieFlagYes } - labels.PubID = effectivePubID(bidReq.Site.Publisher) + labels.PubID = getAccountID(bidReq.Site.Publisher) } if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { diff --git a/exchange/utils.go b/exchange/utils.go index 2131aac5f41..2e9e4dc8f80 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -206,14 +206,15 @@ func prepareSource(req *openrtb.BidRequest, bidder string, sChainsByBidder map[s } // set source - var source openrtb.Source + if req.Source == nil { + req.Source = &openrtb.Source{} + } schain := openrtb_ext.ExtRequestPrebidSChain{ SChain: *selectedSChain, } sourceExt, err := json.Marshal(schain) if err == nil { - source.Ext = sourceExt - req.Source = &source + req.Source.Ext = sourceExt } } From 0c96441954e9443d11a591daa62da86cba14cde1 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Thu, 3 Sep 2020 07:50:18 -0700 Subject: [PATCH 189/318] Add support for Account configuration (PBID-727, #1395) (#1426) --- analytics/core.go | 2 + config/accounts.go | 8 + config/config.go | 44 ++++- config/config_test.go | 17 ++ config/stored_requests.go | 36 +++- endpoints/openrtb2/amp_auction.go | 34 ++-- endpoints/openrtb2/amp_auction_test.go | 14 +- endpoints/openrtb2/auction.go | 74 ++++++-- endpoints/openrtb2/auction_benchmark_test.go | 1 + endpoints/openrtb2/auction_test.go | 172 +++++++++++++++--- .../account-required/valid-acct.json | 67 +++++++ endpoints/openrtb2/video_auction.go | 14 +- endpoints/openrtb2/video_auction_test.go | 7 +- exchange/exchange.go | 8 +- exchange/exchange_test.go | 8 +- exchange/targeting_test.go | 2 +- router/router.go | 8 +- .../backends/db_fetcher/fetcher.go | 4 + .../backends/empty_fetcher/fetcher.go | 5 + .../backends/file_fetcher/fetcher.go | 15 ++ .../backends/file_fetcher/fetcher_test.go | 14 ++ .../file_fetcher/test/accounts/valid.json | 4 + .../backends/http_fetcher/fetcher.go | 4 + stored_requests/config/config.go | 5 +- stored_requests/data/by_id/accounts/test.json | 14 ++ stored_requests/fetcher.go | 10 + stored_requests/fetcher_test.go | 5 + stored_requests/multifetcher.go | 15 ++ stored_requests/multifetcher_test.go | 51 ++++++ 29 files changed, 576 insertions(+), 86 deletions(-) create mode 100644 config/accounts.go create mode 100644 endpoints/openrtb2/sample-requests/account-required/valid-acct.json create mode 100644 stored_requests/backends/file_fetcher/test/accounts/valid.json create mode 100644 stored_requests/data/by_id/accounts/test.json diff --git a/analytics/core.go b/analytics/core.go index 6fd5139fd3d..737d133487e 100644 --- a/analytics/core.go +++ b/analytics/core.go @@ -2,6 +2,7 @@ package analytics import ( "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/usersync" ) @@ -28,6 +29,7 @@ type AuctionObject struct { Errors []error Request *openrtb.BidRequest Response *openrtb.BidResponse + Account *config.Account } //Loggable object of a transaction at /openrtb2/amp endpoint diff --git a/config/accounts.go b/config/accounts.go new file mode 100644 index 00000000000..11c2e10eb1b --- /dev/null +++ b/config/accounts.go @@ -0,0 +1,8 @@ +package config + +// Account represents a publisher account configuration +type Account struct { + ID string `mapstructure:"id" json:"id"` + Disabled bool `mapstructure:"disabled" json:"disabled"` + CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"` +} diff --git a/config/config.go b/config/config.go index 23cc35719db..59ba55ebe26 100755 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "encoding/json" "errors" "fmt" "net/url" @@ -40,6 +41,7 @@ type Configuration struct { StoredRequests StoredRequests `mapstructure:"stored_requests"` StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"` CategoryMapping StoredRequests `mapstructure:"category_mapping"` + Accounts StoredRequests `mapstructure:"accounts"` // Note that StoredVideo refers to stored video requests, and has nothing to do with caching video creatives. StoredVideo StoredRequests `mapstructure:"stored_video_req"` @@ -65,6 +67,11 @@ type Configuration struct { BlacklistedAcctMap map[string]bool // Is publisher/account ID required to be submitted in the OpenRTB2 request AccountRequired bool `mapstructure:"account_required"` + // AccountDefaults defines default settings for valid accounts that are partially defined + // and provides a way to set global settings that can be overridden at account level. + AccountDefaults Account `mapstructure:"account_defaults"` + // accountDefaultsJSON is the internal serialized form of AccountDefaults used for json merge + accountDefaultsJSON json.RawMessage // Local private file containing SSL certificates PemCertsFile string `mapstructure:"certificates_file"` // Custom headers to handle request timeouts from queueing infrastructure @@ -106,10 +113,11 @@ func (c configErrors) Error() string { func (cfg *Configuration) validate() configErrors { var errs configErrors errs = cfg.AuctionTimeouts.validate(errs) - errs = cfg.StoredRequests.validate("stored_req", errs) - errs = cfg.StoredRequestsAMP.validate("stored_amp_req", errs) - errs = cfg.CategoryMapping.validate("categories", errs) - errs = cfg.StoredVideo.validate("stored_video_req", errs) + errs = cfg.StoredRequests.validate(errs) + errs = cfg.StoredRequestsAMP.validate(errs) + errs = cfg.Accounts.validate(errs) + errs = cfg.CategoryMapping.validate(errs) + errs = cfg.StoredVideo.validate(errs) errs = cfg.Metrics.validate(errs) if cfg.MaxRequestSize < 0 { errs = append(errs, fmt.Errorf("cfg.max_request_size must be >= 0. Got %d", cfg.MaxRequestSize)) @@ -119,6 +127,9 @@ func (cfg *Configuration) validate() configErrors { errs = validateAdapters(cfg.Adapters, errs) errs = cfg.Debug.validate(errs) errs = cfg.ExtCacheURL.validate(errs) + if cfg.AccountDefaults.Disabled { + glog.Warning(`With account_defaults.disabled=true, host-defined accounts must exist and have "disabled":false. All other requests will be rejected.`) + } return errs } @@ -589,6 +600,12 @@ func New(v *viper.Viper) (*Configuration, error) { return nil, err } + // Update account defaults and generate base json for patch + c.AccountDefaults.CacheTTL = c.CacheURL.DefaultTTLs // comment this out to set explicitly in config + if err := c.MarshalAccountDefaults(); err != nil { + return nil, err + } + // To look for a request's publisher_id in the NonStandardPublishers list in // O(1) time, we fill this hash table located in the NonStandardPublisherMap field of GDPR c.GDPR.NonStandardPublisherMap = make(map[string]int) @@ -622,6 +639,20 @@ func New(v *viper.Viper) (*Configuration, error) { return &c, nil } +// MarshalAccountDefaults compiles AccountDefaults into the JSON format used for merge patch +func (cfg *Configuration) MarshalAccountDefaults() error { + var err error + if cfg.accountDefaultsJSON, err = json.Marshal(cfg.AccountDefaults); err != nil { + glog.Warningf("converting %+v to json: %v", cfg.AccountDefaults, err) + } + return err +} + +// AccountDefaultsJSON returns the precompiled JSON form of account_defaults +func (cfg *Configuration) AccountDefaultsJSON() json.RawMessage { + return cfg.accountDefaultsJSON +} + //Allows for protocol relative URL if scheme is empty func (cfg *Cache) GetBaseURL() string { cfg.Scheme = strings.ToLower(cfg.Scheme) @@ -843,6 +874,10 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("stored_video_req.http_events.refresh_rate_seconds", 0) v.SetDefault("stored_video_req.http_events.timeout_ms", 0) + v.SetDefault("accounts.filesystem.enabled", false) + v.SetDefault("accounts.filesystem.directorypath", "./stored_requests/data/by_id") + v.SetDefault("accounts.in_memory_cache.type", "none") + for _, bidder := range openrtb_ext.BidderMap { setBidderDefaults(v, strings.ToLower(string(bidder))) } @@ -976,6 +1011,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("blacklisted_apps", []string{""}) v.SetDefault("blacklisted_accts", []string{""}) v.SetDefault("account_required", false) + v.SetDefault("account_defaults.disabled", false) v.SetDefault("certificates_file", "") v.SetDefault("auto_gen_source_tid", true) diff --git a/config/config_test.go b/config/config_test.go index 23764d216ef..d2e80c7e14c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "errors" "net" "os" "strings" @@ -466,6 +467,10 @@ func TestValidConfig(t *testing.T) { CategoryMapping: StoredRequests{ Files: FileFetcherConfig{Enabled: true}, }, + Accounts: StoredRequests{ + Files: FileFetcherConfig{Enabled: true}, + InMemoryCache: InMemoryCache{Type: "none"}, + }, } resolvedStoredRequestsConfig(&cfg) @@ -640,6 +645,18 @@ func TestValidateDebug(t *testing.T) { assert.NotNil(t, err, "cfg.debug.timeout_notification.sampling_rate should not be allowed to be greater than 1.0, but it was allowed") } +func TestValidateAccountsConfigRestrictions(t *testing.T) { + cfg := newDefaultConfig(t) + cfg.Accounts.Files.Enabled = true + cfg.Accounts.HTTP.Endpoint = "http://localhost" + cfg.Accounts.Postgres.ConnectionInfo.Database = "accounts" + + errs := cfg.validate() + assert.Len(t, errs, 2) + assert.Contains(t, errs, errors.New("accounts.http: retrieving accounts via http not available, use accounts.files")) + assert.Contains(t, errs, errors.New("accounts.postgres: retrieving accounts via postgres not available, use accounts.files")) +} + func newDefaultConfig(t *testing.T) *Configuration { v := viper.New() SetupViper(v, "") diff --git a/config/stored_requests.go b/config/stored_requests.go index b63073fede7..61db7eb03d0 100644 --- a/config/stored_requests.go +++ b/config/stored_requests.go @@ -18,8 +18,20 @@ const ( CategoryDataType DataType = "Category" VideoDataType DataType = "Video" AMPRequestDataType DataType = "AMP Request" + AccountDataType DataType = "Account" ) +// Section returns the config section this type is defined in +func (sr *StoredRequests) Section() string { + return map[DataType]string{ + RequestDataType: "stored_requests", + CategoryDataType: "categories", + VideoDataType: "stored_video_req", + AMPRequestDataType: "stored_amp_req", + AccountDataType: "accounts", + }[sr.dataType] +} + func (sr *StoredRequests) DataType() DataType { return sr.dataType } @@ -109,34 +121,42 @@ func resolvedStoredRequestsConfig(cfg *Configuration) { cfg.StoredRequestsAMP.dataType = AMPRequestDataType cfg.StoredVideo.dataType = VideoDataType cfg.CategoryMapping.dataType = CategoryDataType + cfg.Accounts.dataType = AccountDataType return } -func (cfg *StoredRequests) validate(section string, errs configErrors) configErrors { - errs = cfg.Postgres.validate(section, errs) +func (cfg *StoredRequests) validate(errs configErrors) configErrors { + if cfg.DataType() == AccountDataType && cfg.HTTP.Endpoint != "" { + errs = append(errs, fmt.Errorf("%s.http: retrieving accounts via http not available, use accounts.files", cfg.Section())) + } + if cfg.DataType() == AccountDataType && cfg.Postgres.ConnectionInfo.Database != "" { + errs = append(errs, fmt.Errorf("%s.postgres: retrieving accounts via postgres not available, use accounts.files", cfg.Section())) + } else { + errs = cfg.Postgres.validate(cfg.Section(), errs) + } // Categories do not use cache so none of the following checks apply - if cfg.dataType == CategoryDataType { + if cfg.DataType() == CategoryDataType { return errs } if cfg.InMemoryCache.Type == "none" { if cfg.CacheEvents.Enabled { - errs = append(errs, fmt.Errorf("%s: cache_events must be disabled if in_memory_cache=none", section)) + errs = append(errs, fmt.Errorf("%s: cache_events must be disabled if in_memory_cache=none", cfg.Section())) } if cfg.HTTPEvents.RefreshRate != 0 { - errs = append(errs, fmt.Errorf("%s: http_events.refresh_rate_seconds must be 0 if in_memory_cache=none", section)) + errs = append(errs, fmt.Errorf("%s: http_events.refresh_rate_seconds must be 0 if in_memory_cache=none", cfg.Section())) } if cfg.Postgres.PollUpdates.Query != "" { - errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.query must be empty if in_memory_cache=none", section)) + errs = append(errs, fmt.Errorf("%s: postgres.poll_for_updates.query must be empty if in_memory_cache=none", cfg.Section())) } if cfg.Postgres.CacheInitialization.Query != "" { - errs = append(errs, fmt.Errorf("%s: postgres.initialize_caches.query must be empty if in_memory_cache=none", section)) + errs = append(errs, fmt.Errorf("%s: postgres.initialize_caches.query must be empty if in_memory_cache=none", cfg.Section())) } } - errs = cfg.InMemoryCache.validate(section, errs) + errs = cfg.InMemoryCache.validate(cfg.Section(), errs) return errs } diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index f5b72e029e0..54f4706902d 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -44,6 +44,7 @@ func NewAmpEndpoint( ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, + accounts stored_requests.AccountFetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, @@ -53,7 +54,7 @@ func NewAmpEndpoint( bidderMap map[string]openrtb_ext.BidderName, ) (httprouter.Handle, error) { - if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { + if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || met == nil { return nil, errors.New("NewAmpEndpoint requires non-nil arguments.") } @@ -69,6 +70,7 @@ func NewAmpEndpoint( validator, requestsById, empty_fetcher.EmptyFetcher{}, + accounts, categories, cfg, met, @@ -155,26 +157,30 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h labels.CookieFlag = pbsmetrics.CookieFlagYes } labels.PubID = getAccountID(req.Site.Publisher) - - // Blacklist account now that we have resolved the value - if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { - errL = append(errL, acctIdErr) - errCode := errortypes.ReadCode(acctIdErr) - if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode { - w.WriteHeader(http.StatusServiceUnavailable) - labels.RequestStatus = pbsmetrics.RequestStatusBlacklisted - } else { - w.WriteHeader(http.StatusBadRequest) - labels.RequestStatus = pbsmetrics.RequestStatusBadInput + // Look up account now that we have resolved the pubID value + account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + if len(acctIDErrs) > 0 { + errL = append(errL, acctIDErrs...) + httpStatus := http.StatusBadRequest + metricsStatus := pbsmetrics.RequestStatusBadInput + for _, er := range errL { + errCode := errortypes.ReadCode(er) + if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode { + httpStatus = http.StatusServiceUnavailable + metricsStatus = pbsmetrics.RequestStatusBlacklisted + break + } } + w.WriteHeader(httpStatus) + labels.RequestStatus = metricsStatus for _, err := range errortypes.FatalOnly(errL) { w.Write([]byte(fmt.Sprintf("Invalid request format: %s\n", err.Error()))) } - ao.Errors = append(ao.Errors, acctIdErr) + ao.Errors = append(ao.Errors, acctIDErrs...) return } - response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories, nil) + response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, account, &deps.categories, nil) ao.AuctionResponse = response if err != nil { diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index be6735c4c39..57e0a7b447d 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -47,6 +47,7 @@ func TestGoodAmpRequests(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{goodRequests}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -100,6 +101,7 @@ func TestAMPPageInfo(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -205,6 +207,7 @@ func TestGDPRConsent(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -358,6 +361,7 @@ func TestCCPAConsent(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -417,6 +421,7 @@ func TestNoConsent(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -463,6 +468,7 @@ func TestInvalidConsent(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -547,6 +553,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -599,6 +606,7 @@ func TestAMPSiteExt(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{stored}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -639,6 +647,7 @@ func TestAmpBadRequests(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{badRequests}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -670,6 +679,7 @@ func TestAmpDebug(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{requests}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -743,6 +753,7 @@ func TestQueryParamOverrides(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{requests}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -895,6 +906,7 @@ func (s formatOverrideSpec) execute(t *testing.T) { newParamsValidator(t), &mockAmpStoredReqFetcher{requests}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -952,7 +964,7 @@ var expectedErrorsFromHoldAuction map[openrtb_ext.BidderName][]openrtb_ext.ExtBi }, } -func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (m *mockAmpExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest response := &openrtb.BidResponse{ diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index bad2f8aae5b..bc0cd90073f 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -46,9 +46,9 @@ var ( dntEnabled int8 = 1 ) -func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName) (httprouter.Handle, error) { +func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, accounts stored_requests.AccountFetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName) (httprouter.Handle, error) { - if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { + if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || met == nil { return nil, errors.New("NewEndpoint requires non-nil arguments.") } @@ -64,6 +64,7 @@ func NewEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidato validator, requestsById, empty_fetcher.EmptyFetcher{}, + accounts, categories, cfg, met, @@ -82,6 +83,7 @@ type endpointDeps struct { paramsValidator openrtb_ext.BidderParamValidator storedReqFetcher stored_requests.Fetcher videoFetcher stored_requests.Fetcher + accounts stored_requests.AccountFetcher categories stored_requests.CategoryFetcher cfg *config.Configuration metricsEngine pbsmetrics.MetricsEngine @@ -153,15 +155,18 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http labels.PubID = getAccountID(req.Site.Publisher) } - if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { - errL = append(errL, acctIdErr) + // Look up account now that we have resolved the pubID value + account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + if len(acctIDErrs) > 0 { + errL = append(errL, acctIDErrs...) writeError(errL, w, &labels) return } - response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories, nil) + response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, account, &deps.categories, nil) ao.Request = req ao.Response = response + ao.Account = account if err != nil { labels.RequestStatus = pbsmetrics.RequestStatusErr w.WriteHeader(http.StatusInternalServerError) @@ -1305,14 +1310,55 @@ func getAccountID(pub *openrtb.Publisher) string { return pbsmetrics.PublisherUnknown } -func validateAccount(cfg *config.Configuration, pubID string) error { - var err error = nil - if cfg.AccountRequired && pubID == pbsmetrics.PublisherUnknown { - // If specified in the configuration, discard requests that don't come with an account ID. - err = error(&errortypes.AcctRequired{Message: fmt.Sprintf("Prebid-server has been configured to discard requests that don't come with an Account ID. Please reach out to the prebid server host.")}) - } else if _, found := cfg.BlacklistedAcctMap[pubID]; found { - // Blacklist account now that we have resolved the value - err = error(&errortypes.BlacklistedAcct{Message: fmt.Sprintf("Prebid-server has blacklisted Account ID: %s, please reach out to the prebid server host.", pubID)}) +func (deps *endpointDeps) getAccount(ctx context.Context, pubID string) (account *config.Account, errs []error) { + // Check BlacklistedAcctMap until we have deprecated it + if _, found := deps.cfg.BlacklistedAcctMap[pubID]; found { + return nil, []error{&errortypes.BlacklistedAcct{ + Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", pubID), + }} + } + if deps.cfg.AccountRequired && pubID == pbsmetrics.PublisherUnknown { + return nil, []error{&errortypes.AcctRequired{ + Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), + }} + } + if accountJSON, accErrs := deps.accounts.FetchAccount(ctx, pubID); len(accErrs) > 0 || accountJSON == nil { + // pubID does not reference a valid account + if len(accErrs) > 0 { + errs = append(errs, errs...) + } + if deps.cfg.AccountRequired && deps.cfg.AccountDefaults.Disabled { + errs = append(errs, &errortypes.AcctRequired{ + Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), + }) + return nil, errs + } + // Make a copy of AccountDefaults instead of taking a reference, + // to preserve original pubID in case is needed to check NonStandardPublisherMap + pubAccount := deps.cfg.AccountDefaults + pubAccount.ID = pubID + account = &pubAccount + } else { + // pubID resolved to a valid account, merge with AccountDefaults for a complete config + account = &config.Account{} + completeJSON, err := jsonpatch.MergePatch(deps.cfg.AccountDefaultsJSON(), accountJSON) + if err == nil { + err = json.Unmarshal(completeJSON, account) + } + if err != nil { + errs = append(errs, err) + return nil, errs + } + // Fill in ID if needed, so it can be left out of account definition + if len(account.ID) == 0 { + account.ID = pubID + } } - return err + if account.Disabled { + errs = append(errs, &errortypes.BlacklistedAcct{ + Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", pubID), + }) + return nil, errs + } + return } diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go index fba0daecea8..09af23af103 100644 --- a/endpoints/openrtb2/auction_benchmark_test.go +++ b/endpoints/openrtb2/auction_benchmark_test.go @@ -83,6 +83,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) { paramValidator, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 6baab8d500f..72da2a36953 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -38,16 +38,17 @@ const maxSize = 1024 * 256 // Struct of data for the general purpose auction tester type getResponseFromDirectory struct { - dir string - file string - payloadGetter func(*testing.T, []byte) []byte - messageGetter func(*testing.T, []byte) []byte - expectedCode int - aliased bool - disabledBidders []string - adaptersConfig map[string]config.Adapter - accountReq bool - description string + dir string + file string + payloadGetter func(*testing.T, []byte) []byte + messageGetter func(*testing.T, []byte) []byte + expectedCode int + aliased bool + disabledBidders []string + adaptersConfig map[string]config.Adapter + accountReq bool + accountDefaultDisabled bool + description string } // TestExplicitUserId makes sure that the cookie's ID doesn't override an explicit value sent in the request. @@ -103,7 +104,7 @@ func TestExplicitUserId(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - endpoint, _ := NewEndpoint(ex, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, cfg, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + endpoint, _ := NewEndpoint(ex, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, cfg, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) endpoint(httptest.NewRecorder(), request, nil) @@ -255,7 +256,7 @@ func TestRejectAccountRequired(t *testing.T) { accountReq: true, }, { - // Account is required, was provided and is not in the blacklisted accounts map + // Account is required, was provided, not blacklisted, is not defined by host dir: "sample-requests/account-required", file: "with-acct.json", payloadGetter: getRequestPayload, @@ -264,6 +265,28 @@ func TestRejectAccountRequired(t *testing.T) { aliased: true, accountReq: true, }, + { + // Account is required, was provided, not blacklisted, is not defined by host + // but strict validation is in force because default account settings are disabled. + dir: "sample-requests/account-required", + file: "with-acct.json", + payloadGetter: getRequestPayload, + messageGetter: nilReturner, + expectedCode: http.StatusBadRequest, + aliased: true, + accountReq: true, + accountDefaultDisabled: true, + }, + { + // Account is required, was provided, not blacklisted and is a valid account + dir: "sample-requests/account-required", + file: "valid-acct.json", + payloadGetter: getRequestPayload, + messageGetter: nilReturner, + expectedCode: http.StatusOK, + aliased: true, + accountReq: true, + }, { // Account is required, was provided in request and is found in the blacklisted accounts map dir: "sample-requests/blacklisted", @@ -346,12 +369,23 @@ func (gr *getResponseFromDirectory) doRequest(t *testing.T, requestData []byte) // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) + cfg := config.Configuration{ + MaxRequestSize: maxSize, + BlacklistedApps: []string{"spam_app"}, + BlacklistedAppMap: map[string]bool{"spam_app": true}, + BlacklistedAccts: []string{"bad_acct"}, + BlacklistedAcctMap: map[string]bool{"bad_acct": true}, + AccountRequired: gr.accountReq, + AccountDefaults: config.Account{Disabled: gr.accountDefaultDisabled}, + } + assert.NoError(t, cfg.MarshalAccountDefaults()) endpoint, _ := NewEndpoint( &nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, + &mockAccountFetcher{}, empty_fetcher.EmptyFetcher{}, - &config.Configuration{MaxRequestSize: maxSize, BlacklistedApps: []string{"spam_app"}, BlacklistedAppMap: map[string]bool{"spam_app": true}, BlacklistedAccts: []string{"bad_acct"}, BlacklistedAcctMap: map[string]bool{"bad_acct": true}, AccountRequired: gr.accountReq}, + &cfg, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), disabledBidders, @@ -390,7 +424,7 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - endpoint, _ := NewEndpoint(&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), disabledBidders, aliasJSON, bidderMap) + endpoint, _ := NewEndpoint(&nobidExchange{}, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), disabledBidders, aliasJSON, bidderMap) request := httptest.NewRequest("POST", "/openrtb2/auction", bytes.NewReader(requestData)) recorder := httptest.NewRecorder() @@ -454,7 +488,7 @@ func TestNilExchange(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - _, err := NewEndpoint(nil, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + _, err := NewEndpoint(nil, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) if err == nil { t.Errorf("NewEndpoint should return an error when given a nil Exchange.") } @@ -465,7 +499,7 @@ func TestNilValidator(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - _, err := NewEndpoint(&nobidExchange{}, nil, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + _, err := NewEndpoint(&nobidExchange{}, nil, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) if err == nil { t.Errorf("NewEndpoint should return an error when given a nil BidderParamValidator.") } @@ -476,7 +510,7 @@ func TestExchangeError(t *testing.T) { // NewMetrics() will create a new go_metrics MetricsEngine, bypassing the need for a crafted configuration set to support it. // As a side effect this gives us some coverage of the go_metrics piece of the metrics engine. theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) - endpoint, _ := NewEndpoint(&brokenExchange{}, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + endpoint, _ := NewEndpoint(&brokenExchange{}, newParamsValidator(t), empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) request := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json"))) recorder := httptest.NewRecorder() endpoint(recorder, request, nil) @@ -588,7 +622,7 @@ func TestImplicitIPsEndToEnd(t *testing.T) { IPv6PrivateNetworksParsed: test.privateNetworksIPv6, }, } - endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, cfg, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, cfg, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile))) httpReq.Header.Set("X-Forwarded-For", test.xForwardedForHeader) @@ -773,7 +807,7 @@ func TestImplicitDNTEndToEnd(t *testing.T) { metrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) for _, test := range testCases { exchange := &nobidExchange{} - endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) + endpoint, _ := NewEndpoint(exchange, newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, metrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BidderMap) httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, test.reqJSONFile))) httpReq.Header.Set("DNT", test.dntHeader) @@ -846,6 +880,7 @@ func TestStoredRequests(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -885,6 +920,7 @@ func TestOversizedRequest(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(reqBody) - 1)}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -920,6 +956,7 @@ func TestRequestSizeEdgeCase(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(reqBody))}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -953,6 +990,7 @@ func TestNoEncoding(t *testing.T) { newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1028,6 +1066,7 @@ func TestContentType(t *testing.T) { newParamsValidator(t), &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1058,6 +1097,7 @@ func TestDisabledBidder(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{ MaxRequestSize: int64(len(reqBody)), }, @@ -1096,6 +1136,7 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(8096)}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1127,6 +1168,7 @@ func TestCurrencyTrunc(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1171,6 +1213,7 @@ func TestCCPAInvalid(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1223,6 +1266,7 @@ func TestValidateSourceTID(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1267,6 +1311,7 @@ func TestSChainInvalid(t *testing.T) { &mockStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{}, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1487,7 +1532,7 @@ type nobidExchange struct { gotRequest *openrtb.BidRequest } -func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { e.gotRequest = bidRequest return &openrtb.BidResponse{ ID: bidRequest.ID, @@ -1498,7 +1543,7 @@ func (e *nobidExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.Bid type brokenExchange struct{} -func (e *brokenExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (e *brokenExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { return nil, errors.New("Critical, unrecoverable error.") } @@ -1854,11 +1899,27 @@ func (cf mockStoredReqFetcher) FetchRequests(ctx context.Context, requestIDs []s return testStoredRequestData, testStoredImpData, nil } +var mockAccountData = map[string]json.RawMessage{ + "valid_acct": json.RawMessage(`{"disabled":false}`), + "disabled_acct": json.RawMessage(`{"disabled":true}`), +} + +type mockAccountFetcher struct { +} + +func (af mockAccountFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + if account, ok := mockAccountData[accountID]; ok { + return account, nil + } else { + return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} + } +} + type mockExchange struct { lastRequest *openrtb.BidRequest } -func (m *mockExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (m *mockExchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest return &openrtb.BidResponse{ SeatBid: []openrtb.SeatBid{{ @@ -1913,3 +1974,70 @@ type hardcodedResponseIPValidator struct { func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool { return v.response } + +func TestGetAccount(t *testing.T) { + unknown := pbsmetrics.PublisherUnknown + testCases := []struct { + accountID string + // account_required + required bool + // account_defaults.disabled + disabled bool + // expected error, or nil if account should be found + err error + }{ + // Blacklisted account is always rejected even in permissive setup + {accountID: "bad_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, + + // empty pubID + {accountID: unknown, required: false, disabled: false, err: nil}, + {accountID: unknown, required: true, disabled: false, err: &errortypes.AcctRequired{}}, + {accountID: unknown, required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, + + // pubID given but is not a valid host account (does not exist) + {accountID: "not_bad_acct", required: false, disabled: false, err: nil}, + {accountID: "not_bad_acct", required: true, disabled: false, err: nil}, + {accountID: "not_bad_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "not_bad_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, + + // pubID given and matches a valid host account with Disabled: false + {accountID: "valid_acct", required: false, disabled: false, err: nil}, + {accountID: "valid_acct", required: true, disabled: false, err: nil}, + {accountID: "valid_acct", required: false, disabled: true, err: nil}, + {accountID: "valid_acct", required: true, disabled: true, err: nil}, + + // pubID given and matches a host account explicitly disabled (Disabled: true on account json) + {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}}, + } + + for _, test := range testCases { + description := fmt.Sprintf(`ID=%s/required=%t/disabled=%t`, test.accountID, test.required, test.disabled) + t.Run(description, func(t *testing.T) { + deps := &endpointDeps{ + cfg: &config.Configuration{ + BlacklistedAcctMap: map[string]bool{"bad_acct": true}, + AccountRequired: test.required, + AccountDefaults: config.Account{Disabled: test.disabled}, + }, + accounts: &mockAccountFetcher{}, + } + assert.NoError(t, deps.cfg.MarshalAccountDefaults()) + + account, errors := deps.getAccount(context.Background(), test.accountID) + + if test.err == nil { + assert.Empty(t, errors) + assert.Equal(t, test.accountID, account.ID, "account.ID must match requested ID") + assert.Equal(t, false, account.Disabled, "returned account must not be disabled") + } else { + assert.NotEmpty(t, errors, "expected errors but got success") + assert.Nil(t, account, "return account must be nil on error") + assert.IsType(t, test.err, errors[0], "error is of unexpected type") + } + }) + } +} diff --git a/endpoints/openrtb2/sample-requests/account-required/valid-acct.json b/endpoints/openrtb2/sample-requests/account-required/valid-acct.json new file mode 100644 index 00000000000..87d9d1eac37 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/account-required/valid-acct.json @@ -0,0 +1,67 @@ +{ + "description": "This request comes with a valid account id", + "message": "", + + "requestPayload": { + "id": "some-request-id", + "site": { + "publisher": { "id": "valid_acct"}, + "page": "test.somepage.com" + }, + "user": { }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "pmp": { + "deals": [ + { + "id": "some-deal-id" + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "aliases": { + "districtm": "appnexus" + }, + "bidadjustmentfactors": { + "appnexus": 1.01, + "districtm": 0.98, + "rubicon": 0.99 + }, + "cache": { + "bids": {} + }, + "targeting": { + "includewinners": false, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.10 + } + ] + } + } + } + } + } + } + diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index a8e4c28b167..f5494751cc2 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -35,9 +35,9 @@ import ( var defaultRequestTimeout int64 = 5000 -func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, videoFetcher stored_requests.Fetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, cache prebid_cache_client.Client) (httprouter.Handle, error) { +func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamValidator, requestsById stored_requests.Fetcher, videoFetcher stored_requests.Fetcher, accounts stored_requests.AccountFetcher, categories stored_requests.CategoryFetcher, cfg *config.Configuration, met pbsmetrics.MetricsEngine, pbsAnalytics analytics.PBSAnalyticsModule, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, cache prebid_cache_client.Client) (httprouter.Handle, error) { - if ex == nil || validator == nil || requestsById == nil || cfg == nil || met == nil { + if ex == nil || validator == nil || requestsById == nil || accounts == nil || cfg == nil || met == nil { return nil, errors.New("NewVideoEndpoint requires non-nil arguments.") } @@ -55,6 +55,7 @@ func NewVideoEndpoint(ex exchange.Exchange, validator openrtb_ext.BidderParamVal validator, requestsById, videoFetcher, + accounts, categories, cfg, met, @@ -253,13 +254,14 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re labels.PubID = getAccountID(bidReq.Site.Publisher) } - if acctIdErr := validateAccount(deps.cfg, labels.PubID); acctIdErr != nil { - errL := []error{err} - handleError(&labels, w, errL, &vo, &debugLog) + // Look up account now that we have resolved the pubID value + account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + if len(acctIDErrs) > 0 { + handleError(&labels, w, acctIDErrs, &vo, &debugLog) return } //execute auction logic - response, err := deps.ex.HoldAuction(ctx, bidReq, usersyncs, labels, &deps.categories, &debugLog) + response, err := deps.ex.HoldAuction(ctx, bidReq, usersyncs, labels, account, &deps.categories, &debugLog) vo.Request = bidReq vo.Response = response if err != nil { diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index 78715f5c87d..a4e9bfbb61e 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -1168,6 +1168,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *p &mockVideoStoredReqFetcher{}, &mockVideoStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, mockModule, @@ -1210,6 +1211,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { &mockVideoStoredReqFetcher{}, &mockVideoStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1233,6 +1235,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps { &mockVideoStoredReqFetcher{}, &mockVideoStoredReqFetcher{}, empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, theMetrics, analyticsConf.NewPBSAnalytics(&config.Analytics{}), @@ -1275,7 +1278,7 @@ type mockExchangeVideo struct { cache *mockCacheClient } -func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (m *mockExchangeVideo) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest if debugLog != nil && debugLog.Enabled { m.cache.called = true @@ -1311,7 +1314,7 @@ type mockExchangeVideoNoBids struct { cache *mockCacheClient } -func (m *mockExchangeVideoNoBids) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { +func (m *mockExchangeVideoNoBids) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, ids exchange.IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *exchange.DebugLog) (*openrtb.BidResponse, error) { m.lastRequest = bidRequest return &openrtb.BidResponse{ SeatBid: []openrtb.SeatBid{{}}, diff --git a/exchange/exchange.go b/exchange/exchange.go index 59e876697cf..9d1cd20affa 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -37,7 +37,7 @@ const DebugContextKey = ContextKey("debugInfo") // Exchange runs Auctions. Implementations must be threadsafe, and will be shared across many goroutines. type Exchange interface { // HoldAuction executes an OpenRTB v2.5 Auction. - HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) + HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) } // IdFetcher can find the user's ID for a specific Bidder. @@ -54,7 +54,6 @@ type exchange struct { gDPR gdpr.Permissions currencyConverter *currencies.RateConverter UsersyncIfAmbiguous bool - defaultTTLs config.DefaultTTLs privacyConfig config.Privacy eeaCountries map[string]struct{} } @@ -89,7 +88,6 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con e.gDPR = gDPR e.currencyConverter = currencyConverter e.UsersyncIfAmbiguous = cfg.GDPR.UsersyncIfAmbiguous - e.defaultTTLs = cfg.CacheURL.DefaultTTLs e.privacyConfig = config.Privacy{ CCPA: cfg.CCPA, GDPR: cfg.GDPR, @@ -98,7 +96,7 @@ func NewExchange(client *http.Client, cache prebid_cache_client.Client, cfg *con return e } -func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) { +func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidRequest, usersyncs IdFetcher, labels pbsmetrics.Labels, account *config.Account, categoriesFetcher *stored_requests.CategoryFetcher, debugLog *DebugLog) (*openrtb.BidResponse, error) { requestExt, err := extractBidRequestExt(bidRequest) if err != nil { @@ -203,7 +201,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque } } - cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &e.defaultTTLs, bidCategory, debugLog) + cacheErrs := auc.doCache(ctx, e.cache, targData, bidRequest, 60, &account.CacheTTL, bidCategory, debugLog) if len(cacheErrs) > 0 { errs = append(errs, cacheErrs...) } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index efabb845211..21003b669ed 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -248,7 +248,7 @@ func TestDebugBehaviour(t *testing.T) { } // Run test - outBidResponse, err := e.HoldAuction(context.Background(), bidRequest, &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) + outBidResponse, err := e.HoldAuction(context.Background(), bidRequest, &emptyUsersync{}, pbsmetrics.Labels{}, &config.Account{}, &categoriesFetcher, nil) // Assert no HoldAuction error assert.NoErrorf(t, err, "%s. ex.HoldAuction returned an error: %v \n", test.desc, err) @@ -752,7 +752,7 @@ func TestRaceIntegration(t *testing.T) { theMetrics := pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}) currencyConverter := currencies.NewRateConverter(&http.Client{}, "", time.Duration(0)) ex := NewExchange(server.Client(), &wellBehavedCache{}, cfg, theMetrics, adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencyConverter) - _, err := ex.HoldAuction(context.Background(), newRaceCheckingRequest(t), &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) + _, err := ex.HoldAuction(context.Background(), newRaceCheckingRequest(t), &emptyUsersync{}, pbsmetrics.Labels{}, &config.Account{}, &categoriesFetcher, nil) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -939,7 +939,7 @@ func TestPanicRecoveryHighLevel(t *testing.T) { if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - _, err := e.HoldAuction(context.Background(), request, &emptyUsersync{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) + _, err := e.HoldAuction(context.Background(), request, &emptyUsersync{}, pbsmetrics.Labels{}, &config.Account{}, &categoriesFetcher, nil) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -1055,7 +1055,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { *debugLog = *spec.DebugLog debugLog.Regexp = regexp.MustCompile(`[<>]`) } - bid, err := ex.HoldAuction(context.Background(), &spec.IncomingRequest.OrtbRequest, mockIdFetcher(spec.IncomingRequest.Usersyncs), pbsmetrics.Labels{}, &categoriesFetcher, debugLog) + bid, err := ex.HoldAuction(context.Background(), &spec.IncomingRequest.OrtbRequest, mockIdFetcher(spec.IncomingRequest.Usersyncs), pbsmetrics.Labels{}, &config.Account{}, &categoriesFetcher, debugLog) responseTimes := extractResponseTimes(t, filename, bid) for _, bidderName := range biddersInAuction { if _, ok := responseTimes[bidderName]; !ok { diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index e596e5aa215..aaa75411ee4 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -108,7 +108,7 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - bidResp, err := ex.HoldAuction(context.Background(), req, &mockFetcher{}, pbsmetrics.Labels{}, &categoriesFetcher, nil) + bidResp, err := ex.HoldAuction(context.Background(), req, &mockFetcher{}, pbsmetrics.Labels{}, &config.Account{}, &categoriesFetcher, nil) if err != nil { t.Fatalf("Unexpected errors running auction: %v", err) diff --git a/router/router.go b/router/router.go index 30936705a22..4826f0e5feb 100644 --- a/router/router.go +++ b/router/router.go @@ -211,7 +211,7 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r // Metrics engine r.MetricsEngine = metricsConf.NewMetricsEngine(cfg, legacyBidderList) - db, shutdown, fetcher, ampFetcher, categoriesFetcher, videoFetcher := storedRequestsConf.NewStoredRequests(cfg, r.MetricsEngine, generalHttpClient, r.Router) + db, shutdown, fetcher, ampFetcher, accounts, categoriesFetcher, videoFetcher := storedRequestsConf.NewStoredRequests(cfg, r.MetricsEngine, generalHttpClient, r.Router) // todo(zachbadgett): better shutdown r.Shutdown = shutdown @@ -243,19 +243,19 @@ func New(cfg *config.Configuration, rateConvertor *currencies.RateConverter) (r cacheClient := pbc.NewClient(cacheHttpClient, &cfg.CacheURL, &cfg.ExtCacheURL, r.MetricsEngine) theExchange := exchange.NewExchange(generalHttpClient, cacheClient, cfg, r.MetricsEngine, bidderInfos, gdprPerms, rateConvertor) - openrtbEndpoint, err := openrtb2.NewEndpoint(theExchange, paramsValidator, fetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) + openrtbEndpoint, err := openrtb2.NewEndpoint(theExchange, paramsValidator, fetcher, accounts, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) if err != nil { glog.Fatalf("Failed to create the openrtb endpoint handler. %v", err) } - ampEndpoint, err := openrtb2.NewAmpEndpoint(theExchange, paramsValidator, ampFetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) + ampEndpoint, err := openrtb2.NewAmpEndpoint(theExchange, paramsValidator, ampFetcher, accounts, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap) if err != nil { glog.Fatalf("Failed to create the amp endpoint handler. %v", err) } - videoEndpoint, err := openrtb2.NewVideoEndpoint(theExchange, paramsValidator, fetcher, videoFetcher, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap, cacheClient) + videoEndpoint, err := openrtb2.NewVideoEndpoint(theExchange, paramsValidator, fetcher, videoFetcher, accounts, categoriesFetcher, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBiddersMap, cacheClient) if err != nil { glog.Fatalf("Failed to create the video endpoint handler. %v", err) } diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go index 223067c917e..d8cf132d25b 100644 --- a/stored_requests/backends/db_fetcher/fetcher.go +++ b/stored_requests/backends/db_fetcher/fetcher.go @@ -93,6 +93,10 @@ func (fetcher *dbFetcher) FetchRequests(ctx context.Context, requestIDs []string return storedRequestData, storedImpData, errs } +func (fetcher *dbFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} +} + func (fetcher *dbFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { return "", nil } diff --git a/stored_requests/backends/empty_fetcher/fetcher.go b/stored_requests/backends/empty_fetcher/fetcher.go index 25e8ead434b..ee6b98b3b2e 100644 --- a/stored_requests/backends/empty_fetcher/fetcher.go +++ b/stored_requests/backends/empty_fetcher/fetcher.go @@ -3,6 +3,7 @@ package empty_fetcher import ( "context" "encoding/json" + "github.com/prebid/prebid-server/stored_requests" ) @@ -27,6 +28,10 @@ func (fetcher EmptyFetcher) FetchRequests(ctx context.Context, requestIDs []stri return } +func (fetcher EmptyFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} +} + func (fetcher EmptyFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { return "", nil } diff --git a/stored_requests/backends/file_fetcher/fetcher.go b/stored_requests/backends/file_fetcher/fetcher.go index 60853f65da7..2d3b00657b9 100644 --- a/stored_requests/backends/file_fetcher/fetcher.go +++ b/stored_requests/backends/file_fetcher/fetcher.go @@ -33,6 +33,21 @@ func (fetcher *eagerFetcher) FetchRequests(ctx context.Context, requestIDs []str return storedRequests, storedImpressions, errs } +// FetchAccount fetches the host account configuration for a publisher +func (fetcher *eagerFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + if len(accountID) == 0 { + return nil, []error{fmt.Errorf("Cannot look up an empty accountID")} + } + accountJSON, ok := fetcher.FileSystem.Directories["accounts"].Files[accountID] + if !ok { + return nil, []error{stored_requests.NotFoundError{ + ID: accountID, + DataType: "Account", + }} + } + return accountJSON, nil +} + func (fetcher *eagerFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { fileName := primaryAdServer diff --git a/stored_requests/backends/file_fetcher/fetcher_test.go b/stored_requests/backends/file_fetcher/fetcher_test.go index 2429a77cd25..a145a3b43a2 100644 --- a/stored_requests/backends/file_fetcher/fetcher_test.go +++ b/stored_requests/backends/file_fetcher/fetcher_test.go @@ -24,6 +24,20 @@ func TestFileFetcher(t *testing.T) { validateImp(t, storedImps) } +func TestAccountFetcher(t *testing.T) { + fetcher, err := NewFileFetcher("./test") + assert.NoError(t, err, "Failed to create test fetcher") + + account, errs := fetcher.FetchAccount(context.Background(), "valid") + assertErrorCount(t, 0, errs) + assert.JSONEq(t, `{"disabled":false, "id":"valid"}`, string(account)) + + account, errs = fetcher.FetchAccount(context.Background(), "nonexistent") + assertErrorCount(t, 1, errs) + assert.Error(t, errs[0]) + assert.Equal(t, stored_requests.NotFoundError{"nonexistent", "Account"}, errs[0]) +} + func TestInvalidDirectory(t *testing.T) { _, err := NewFileFetcher("./nonexistant-directory") if err == nil { diff --git a/stored_requests/backends/file_fetcher/test/accounts/valid.json b/stored_requests/backends/file_fetcher/test/accounts/valid.json new file mode 100644 index 00000000000..2c8bd12af3c --- /dev/null +++ b/stored_requests/backends/file_fetcher/test/accounts/valid.json @@ -0,0 +1,4 @@ +{ + "id": "valid", + "disabled": false +} diff --git a/stored_requests/backends/http_fetcher/fetcher.go b/stored_requests/backends/http_fetcher/fetcher.go index b7e42c9e6cf..d533d5315ab 100644 --- a/stored_requests/backends/http_fetcher/fetcher.go +++ b/stored_requests/backends/http_fetcher/fetcher.go @@ -81,6 +81,10 @@ func (fetcher *HttpFetcher) FetchRequests(ctx context.Context, requestIDs []stri return } +func (fetcher *HttpFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} +} + func (fetcher *HttpFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { if fetcher.Categories == nil { fetcher.Categories = make(map[string]map[string]stored_requests.Category) diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go index 8f06efcb32b..e81d9667a73 100644 --- a/stored_requests/config/config.go +++ b/stored_requests/config/config.go @@ -106,7 +106,7 @@ func CreateStoredRequests(cfg *config.StoredRequests, metricsEngine pbsmetrics.M // // As a side-effect, it will add some endpoints to the router if the config calls for it. // In the future we should look for ways to simplify this so that it's not doing two things. -func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, client *http.Client, router *httprouter.Router) (db *sql.DB, shutdown func(), fetcher stored_requests.Fetcher, ampFetcher stored_requests.Fetcher, categoriesFetcher stored_requests.CategoryFetcher, videoFetcher stored_requests.Fetcher) { +func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.MetricsEngine, client *http.Client, router *httprouter.Router) (db *sql.DB, shutdown func(), fetcher stored_requests.Fetcher, ampFetcher stored_requests.Fetcher, accountsFetcher stored_requests.AccountFetcher, categoriesFetcher stored_requests.CategoryFetcher, videoFetcher stored_requests.Fetcher) { // TODO: Switch this to be set in config defaults //if cfg.CategoryMapping.CacheEvents.Enabled && cfg.CategoryMapping.CacheEvents.Endpoint == "" { // cfg.CategoryMapping.CacheEvents.Endpoint = "/storedrequest/categorymapping" @@ -118,6 +118,7 @@ func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.Metri fetcher2, shutdown2 := CreateStoredRequests(&cfg.StoredRequestsAMP, metricsEngine, client, router, &dbc) fetcher3, shutdown3 := CreateStoredRequests(&cfg.CategoryMapping, metricsEngine, client, router, &dbc) fetcher4, shutdown4 := CreateStoredRequests(&cfg.StoredVideo, metricsEngine, client, router, &dbc) + fetcher5, shutdown5 := CreateStoredRequests(&cfg.Accounts, metricsEngine, client, router, &dbc) db = dbc.db @@ -125,12 +126,14 @@ func NewStoredRequests(cfg *config.Configuration, metricsEngine pbsmetrics.Metri ampFetcher = fetcher2.(stored_requests.Fetcher) categoriesFetcher = fetcher3.(stored_requests.CategoryFetcher) videoFetcher = fetcher4.(stored_requests.Fetcher) + accountsFetcher = fetcher5.(stored_requests.AccountFetcher) shutdown = func() { shutdown1() shutdown2() shutdown3() shutdown4() + shutdown5() } return diff --git a/stored_requests/data/by_id/accounts/test.json b/stored_requests/data/by_id/accounts/test.json new file mode 100644 index 00000000000..76bafff7f1c --- /dev/null +++ b/stored_requests/data/by_id/accounts/test.json @@ -0,0 +1,14 @@ +{ + "id": "test", + "name": "test account", + "disabled": true, + "cache_ttl": { + "banner": 600, + "video": 3600, + "native": 3600, + "audio": 3600 + }, + "events": { + "enabled": true + } +} diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go index 23fdb6b4925..a31b9989bd0 100644 --- a/stored_requests/fetcher.go +++ b/stored_requests/fetcher.go @@ -25,6 +25,11 @@ type Fetcher interface { FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) } +type AccountFetcher interface { + // FetchAccount fetches the host account configuration for a publisher + FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) +} + type CategoryFetcher interface { // FetchCategories fetches the ad-server/publisher specific category for the given IAB category FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) @@ -33,6 +38,7 @@ type CategoryFetcher interface { // AllFetcher is an interface that encapsulates both the original Fetcher and the CategoryFetcher type AllFetcher interface { FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) + FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) } @@ -181,6 +187,10 @@ func (f *fetcherWithCache) FetchRequests(ctx context.Context, requestIDs []strin return } +func (f *fetcherWithCache) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + return f.fetcher.FetchAccount(ctx, accountID) +} + func (f *fetcherWithCache) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { return "", nil } diff --git a/stored_requests/fetcher_test.go b/stored_requests/fetcher_test.go index c1040acdb90..1928d1165db 100644 --- a/stored_requests/fetcher_test.go +++ b/stored_requests/fetcher_test.go @@ -215,6 +215,11 @@ func (f *mockFetcher) FetchRequests(ctx context.Context, requestIDs []string, im return args.Get(0).(map[string]json.RawMessage), args.Get(1).(map[string]json.RawMessage), args.Get(2).([]error) } +func (a *mockFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + args := a.Called(ctx, accountID) + return args.Get(0).(json.RawMessage), args.Get(1).([]error) +} + func (f *mockFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { return "", nil } diff --git a/stored_requests/multifetcher.go b/stored_requests/multifetcher.go index 24cf848448c..2d08fd45337 100644 --- a/stored_requests/multifetcher.go +++ b/stored_requests/multifetcher.go @@ -36,6 +36,21 @@ func (mf MultiFetcher) FetchRequests(ctx context.Context, requestIDs []string, i return } +func (mf MultiFetcher) FetchAccount(ctx context.Context, accountID string) (account json.RawMessage, errs []error) { + for _, f := range mf { + if af, ok := f.(AccountFetcher); ok { + if account, accErrs := af.FetchAccount(ctx, accountID); len(accErrs) == 0 { + return account, nil + } else { + accErrs = dropMissingIDs(accErrs) + errs = append(errs, accErrs...) + } + } + } + errs = append(errs, NotFoundError{accountID, "Account"}) + return nil, errs +} + func (mf MultiFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { for _, f := range mf { if cf, ok := f.(CategoryFetcher); ok { diff --git a/stored_requests/multifetcher_test.go b/stored_requests/multifetcher_test.go index e703c2c9dcc..5035cfba82e 100644 --- a/stored_requests/multifetcher_test.go +++ b/stored_requests/multifetcher_test.go @@ -125,3 +125,54 @@ func TestOtherError(t *testing.T) { assert.JSONEq(t, `{"req_id": "def"}`, string(reqData["def"]), "MultiFetcher should return the right request data") assert.JSONEq(t, `{"imp_id": "imp-1"}`, string(impData["imp-1"]), "MultiFetcher should return the right imp data") } + +func TestMultiFetcherAccountFoundInFirstFetcher(t *testing.T) { + f1 := &mockFetcher{} + f2 := &mockFetcher{} + fetcher := &MultiFetcher{f1, f2} + ctx := context.Background() + + f1.On("FetchAccount", ctx, "ONE").Once().Return(json.RawMessage(`{"id": "ONE"}`), []error{}) + + account, errs := fetcher.FetchAccount(ctx, "ONE") + + f1.AssertExpectations(t) + f2.AssertNotCalled(t, "FetchAccount") + assert.Empty(t, errs) + assert.JSONEq(t, `{"id": "ONE"}`, string(account)) +} + +func TestMultiFetcherAccountFoundInSecondFetcher(t *testing.T) { + f1 := &mockFetcher{} + f2 := &mockFetcher{} + fetcher := &MultiFetcher{f1, f2} + ctx := context.Background() + + f1.On("FetchAccount", ctx, "TWO").Once().Return(json.RawMessage(``), []error{NotFoundError{"TWO", "Account"}}) + f2.On("FetchAccount", ctx, "TWO").Once().Return(json.RawMessage(`{"id": "TWO"}`), []error{}) + + account, errs := fetcher.FetchAccount(ctx, "TWO") + + f1.AssertExpectations(t) + f2.AssertExpectations(t) + assert.Empty(t, errs) + assert.JSONEq(t, `{"id": "TWO"}`, string(account)) +} + +func TestMultiFetcherAccountNotFound(t *testing.T) { + f1 := &mockFetcher{} + f2 := &mockFetcher{} + fetcher := &MultiFetcher{f1, f2} + ctx := context.Background() + + f1.On("FetchAccount", ctx, "MISSING").Once().Return(json.RawMessage(``), []error{NotFoundError{"TWO", "Account"}}) + f2.On("FetchAccount", ctx, "MISSING").Once().Return(json.RawMessage(``), []error{NotFoundError{"TWO", "Account"}}) + + account, errs := fetcher.FetchAccount(ctx, "MISSING") + + f1.AssertExpectations(t) + f2.AssertExpectations(t) + assert.Len(t, errs, 1) + assert.Nil(t, account) + assert.EqualError(t, errs[0], NotFoundError{"MISSING", "Account"}.Error()) +} From 44310b6f329813cc0f01747ea2e5c7db4f3975f9 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Tue, 8 Sep 2020 10:19:44 -0400 Subject: [PATCH 190/318] Minor changes to accounts test coverage (#1475) --- endpoints/openrtb2/auction_test.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 72da2a36953..53fea2e0500 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -279,13 +279,14 @@ func TestRejectAccountRequired(t *testing.T) { }, { // Account is required, was provided, not blacklisted and is a valid account - dir: "sample-requests/account-required", - file: "valid-acct.json", - payloadGetter: getRequestPayload, - messageGetter: nilReturner, - expectedCode: http.StatusOK, - aliased: true, - accountReq: true, + dir: "sample-requests/account-required", + file: "valid-acct.json", + payloadGetter: getRequestPayload, + messageGetter: nilReturner, + expectedCode: http.StatusOK, + aliased: true, + accountReq: true, + accountDefaultDisabled: true, }, { // Account is required, was provided in request and is found in the blacklisted accounts map @@ -1996,10 +1997,10 @@ func TestGetAccount(t *testing.T) { {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, // pubID given but is not a valid host account (does not exist) - {accountID: "not_bad_acct", required: false, disabled: false, err: nil}, - {accountID: "not_bad_acct", required: true, disabled: false, err: nil}, - {accountID: "not_bad_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, - {accountID: "not_bad_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, + {accountID: "doesnt_exist_acct", required: false, disabled: false, err: nil}, + {accountID: "doesnt_exist_acct", required: true, disabled: false, err: nil}, + {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "doesnt_exist_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, // pubID given and matches a valid host account with Disabled: false {accountID: "valid_acct", required: false, disabled: false, err: nil}, From d75df46922283b7cbeef8c14774f5bc8163a4c63 Mon Sep 17 00:00:00 2001 From: smithaammassamveettil <39389834+smithaammassamveettil@users.noreply.github.com> Date: Tue, 8 Sep 2020 10:53:49 -0700 Subject: [PATCH 191/318] Brightroll adapter - adding config support (#1461) --- adapters/brightroll/brightroll.go | 81 +++++++++++++++---- adapters/brightroll/brightroll_test.go | 28 ++++++- .../exemplary/banner-native-audio.json | 23 ++++-- .../exemplary/banner-video-native.json | 28 +++++-- .../exemplary/banner-video.json | 24 ++++-- .../exemplary/simple-banner.json | 15 +++- .../exemplary/simple-video.json | 15 +++- .../exemplary/valid-extension.json | 15 +++- .../exemplary/video-and-audio.json | 19 +++-- .../brightrolltest/params/race/banner.json | 2 +- .../brightrolltest/params/race/video.json | 2 +- .../supplemental/invalid-imp.json | 2 +- .../supplemental/invalid-publisher.json | 34 ++++++++ exchange/adapter_map.go | 2 +- 14 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json diff --git a/adapters/brightroll/brightroll.go b/adapters/brightroll/brightroll.go index 0ae95dd303a..83b253a0996 100644 --- a/adapters/brightroll/brightroll.go +++ b/adapters/brightroll/brightroll.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/golang/glog" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" @@ -13,7 +14,20 @@ import ( ) type BrightrollAdapter struct { - URI string + URI string + extraInfo ExtraInfo +} + +type ExtraInfo struct { + Accounts []Account `json:"accounts"` +} + +type Account struct { + ID string `json:"id"` + Badv []string `json:"badv"` + Bcat []string `json:"bcat"` + Battr []int8 `json:"battr"` + BidFloor float64 `json:"bidfloor"` } func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { @@ -55,6 +69,23 @@ func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo errors = append(errors, err) return nil, errors } + + var account *Account + for _, a := range a.extraInfo.Accounts { + if a.ID == brightrollExt.Publisher { + account = &a + break + } + } + + if account == nil { + err = &errortypes.BadInput{ + Message: "Invalid publisher", + } + errors = append(errors, err) + return nil, errors + } + validImpExists := false for i := 0; i < len(request.Imp); i++ { //Brightroll supports only banner and video impressions as of now @@ -65,9 +96,9 @@ func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo bannerCopy.W = &(firstFormat.W) bannerCopy.H = &(firstFormat.H) } - if brightrollExt.Publisher == "adthrive" { - bannerCopy.BAttr = getBlockedCreativetypesForAdThrive() + if len(account.Battr) > 0 { + bannerCopy.BAttr = getBlockedCreativetypes(account.Battr) } request.Imp[i].Banner = &bannerCopy validImpExists = true @@ -75,10 +106,15 @@ func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo validImpExists = true if brightrollExt.Publisher == "adthrive" { videoCopy := *request.Imp[i].Video - videoCopy.BAttr = getBlockedCreativetypesForAdThrive() + if len(account.Battr) > 0 { + videoCopy.BAttr = getBlockedCreativetypes(account.Battr) + } request.Imp[i].Video = &videoCopy } } + if validImpExists && request.Imp[i].BidFloor == 0 && account.BidFloor > 0 { + request.Imp[i].BidFloor = account.BidFloor + } } if !validImpExists { err := &errortypes.BadInput{ @@ -90,8 +126,12 @@ func (a *BrightrollAdapter) MakeRequests(requestIn *openrtb.BidRequest, reqInfo request.AT = 1 //Defaulting to first price auction for all prebid requests - if brightrollExt.Publisher == "adthrive" { - request.BCat = getBlockedCategoriesForAdthrive() + if len(account.Bcat) > 0 { + request.BCat = account.Bcat + } + + if len(account.Badv) > 0 { + request.BAdv = account.Badv } reqJSON, err := json.Marshal(request) if err != nil { @@ -159,13 +199,12 @@ func (a *BrightrollAdapter) MakeBids(internalRequest *openrtb.BidRequest, extern return bidResponse, nil } -//customized request, need following blocked categories -func getBlockedCategoriesForAdthrive() []string { - return []string{"IAB8-5", "IAB8-18", "IAB15-1", "IAB7-30", "IAB14-1", "IAB22-1", "IAB3-7", "IAB7-3", "IAB14-3", "IAB11", "IAB11-1", "IAB11-2", "IAB11-3", "IAB11-4", "IAB11-5", "IAB23", "IAB23-1", "IAB23-2", "IAB23-3", "IAB23-4", "IAB23-5", "IAB23-6", "IAB23-7", "IAB23-8", "IAB23-9", "IAB23-10", "IAB7-39", "IAB9-30", "IAB7-44", "IAB25", "IAB25-1", "IAB25-2", "IAB25-3", "IAB25-4", "IAB25-5", "IAB25-6", "IAB25-7", "IAB26", "IAB26-1", "IAB26-2", "IAB26-3", "IAB26-4"} -} - -func getBlockedCreativetypesForAdThrive() []openrtb.CreativeAttribute { - return []openrtb.CreativeAttribute{openrtb.CreativeAttribute(1), openrtb.CreativeAttribute(2), openrtb.CreativeAttribute(3), openrtb.CreativeAttribute(6), openrtb.CreativeAttribute(9), openrtb.CreativeAttribute(10)} +func getBlockedCreativetypes(attr []int8) []openrtb.CreativeAttribute { + var creativeAttr []openrtb.CreativeAttribute + for i := 0; i < len(attr); i++ { + creativeAttr = append(creativeAttr, openrtb.CreativeAttribute(attr[i])) + } + return creativeAttr } //Adding header fields to request header @@ -189,8 +228,18 @@ func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType { return mediaType } -func NewBrightrollBidder(endpoint string) *BrightrollAdapter { - return &BrightrollAdapter{ - URI: endpoint, +func NewBrightrollBidder(endpoint string, extraAdapterInfo string) *BrightrollAdapter { + + var extraInfo ExtraInfo + + if len(extraAdapterInfo) == 0 { + extraAdapterInfo = "{\"accounts\":[]}" + } + err := json.Unmarshal([]byte(extraAdapterInfo), &extraInfo) + + if err != nil { + glog.Fatalf("Invalid Brightroll extra adapter info: " + err.Error()) + return nil } + return &BrightrollAdapter{URI: endpoint, extraInfo: extraInfo} } diff --git a/adapters/brightroll/brightroll_test.go b/adapters/brightroll/brightroll_test.go index 0a6c2c44567..5f1c64fd16f 100644 --- a/adapters/brightroll/brightroll_test.go +++ b/adapters/brightroll/brightroll_test.go @@ -1,11 +1,37 @@ package brightroll import ( + "github.com/stretchr/testify/assert" "testing" "github.com/prebid/prebid-server/adapters/adapterstest" ) +func TestEmptyConfig(t *testing.T) { + output := NewBrightrollBidder("http://test-bid.ybp.yahoo.com/bid/appnexuspbs", "") + ex := ExtraInfo{ + Accounts: []Account{}, + } + expected := &BrightrollAdapter{ + URI: "http://test-bid.ybp.yahoo.com/bid/appnexuspbs", + extraInfo: ex, + } + assert.Equal(t, expected, output, "") +} + +func TestNonEmptyConfig(t *testing.T) { + output := NewBrightrollBidder("http://test-bid.ybp.yahoo.com/bid/appnexuspbs", "{\"accounts\": [{\"id\": \"test\",\"bidfloor\":0.1}]}") + ex := ExtraInfo{ + Accounts: []Account{{ID: "test", BidFloor: 0.1}}, + } + + expected := &BrightrollAdapter{ + URI: "http://test-bid.ybp.yahoo.com/bid/appnexuspbs", + extraInfo: ex, + } + assert.Equal(t, expected, output, "") +} + func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "brightrolltest", NewBrightrollBidder("http://test-bid.ybp.yahoo.com/bid/appnexuspbs")) + adapterstest.RunJSONBidderTest(t, "brightrolltest", NewBrightrollBidder("http://test-bid.ybp.yahoo.com/bid/appnexuspbs", "{\"accounts\": [{\"id\": \"adthrive\",\"badv\": [], \"bcat\": [\"IAB8-5\",\"IAB8-18\"],\"battr\": [1,2,3], \"bidfloor\":0.0}]}")) } diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json b/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json index e8ef5b688f6..e67a1485e54 100644 --- a/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json +++ b/adapters/brightroll/brightrolltest/exemplary/banner-native-audio.json @@ -18,7 +18,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -30,7 +30,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -43,7 +43,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -52,14 +52,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "banner": { + "battr": [ + 1, + 2, + 3 + ], "format": [ { "w": 300, @@ -75,7 +84,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -87,7 +96,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -100,7 +109,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json b/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json index 4df7ab5df10..0fffbd2fac5 100644 --- a/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json +++ b/adapters/brightroll/brightrolltest/exemplary/banner-video-native.json @@ -18,7 +18,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -30,7 +30,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -44,7 +44,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -53,14 +53,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "banner": { + "battr": [ + 1, + 2, + 3 + ], "format": [ { "w": 300, @@ -76,7 +85,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -88,13 +97,18 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, { "id": "test-imp-video-id", "video": { + "battr": [ + 1, + 2, + 3 + ], "mimes": ["video/mp4"], "protocols": [2, 5], "w": 1024, @@ -102,7 +116,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/banner-video.json b/adapters/brightroll/brightrolltest/exemplary/banner-video.json index 50053102d08..f58dd9b1395 100644 --- a/adapters/brightroll/brightrolltest/exemplary/banner-video.json +++ b/adapters/brightroll/brightrolltest/exemplary/banner-video.json @@ -18,7 +18,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -32,7 +32,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -41,14 +41,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "banner": { + "battr": [ + 1, + 2, + 3 + ], "format": [ { "w": 300, @@ -64,13 +73,18 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, { "id": "test-imp-video-id", "video": { + "battr": [ + 1, + 2, + 3 + ], "mimes": ["video/mp4"], "protocols": [2, 5], "w": 1024, @@ -78,7 +92,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/simple-banner.json b/adapters/brightroll/brightrolltest/exemplary/simple-banner.json index 96fa0cbc9f3..66bc06cf696 100644 --- a/adapters/brightroll/brightrolltest/exemplary/simple-banner.json +++ b/adapters/brightroll/brightrolltest/exemplary/simple-banner.json @@ -18,7 +18,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -28,14 +28,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "banner": { + "battr": [ + 1, + 2, + 3 + ], "format": [ { "w": 300, @@ -51,7 +60,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/simple-video.json b/adapters/brightroll/brightrolltest/exemplary/simple-video.json index f2466b2fd95..3f9b809182d 100644 --- a/adapters/brightroll/brightrolltest/exemplary/simple-video.json +++ b/adapters/brightroll/brightrolltest/exemplary/simple-video.json @@ -12,7 +12,7 @@ }, "ext":{ "bidder":{ - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -22,14 +22,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "video": { + "battr": [ + 1, + 2, + 3 + ], "mimes": ["video/mp4"], "protocols": [2, 5], "w": 1024, @@ -37,7 +46,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/valid-extension.json b/adapters/brightroll/brightrolltest/exemplary/valid-extension.json index 970b4ade63a..da38c62be58 100644 --- a/adapters/brightroll/brightrolltest/exemplary/valid-extension.json +++ b/adapters/brightroll/brightrolltest/exemplary/valid-extension.json @@ -12,7 +12,7 @@ }, "ext":{ "bidder":{ - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -22,14 +22,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-id", "video": { + "battr": [ + 1, + 2, + 3 + ], "mimes": ["video/mp4"], "protocols": [2, 5], "w": 1024, @@ -37,7 +46,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json b/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json index 9f24a471b31..d3295e5bffd 100644 --- a/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json +++ b/adapters/brightroll/brightrolltest/exemplary/video-and-audio.json @@ -12,7 +12,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -25,7 +25,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } @@ -34,14 +34,23 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=cafemom", + "uri": "http://test-bid.ybp.yahoo.com/bid/appnexuspbs?publisher=adthrive", "body": { "id": "test-request-id", "at":1, + "bcat": [ + "IAB8-5", + "IAB8-18" + ], "imp": [ { "id": "test-imp-video-id", "video": { + "battr": [ + 1, + 2, + 3 + ], "mimes": ["video/mp4"], "protocols": [2, 5], "w": 1024, @@ -49,7 +58,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, @@ -62,7 +71,7 @@ }, "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } } diff --git a/adapters/brightroll/brightrolltest/params/race/banner.json b/adapters/brightroll/brightrolltest/params/race/banner.json index 91517e36fd8..6eb4ec6a337 100644 --- a/adapters/brightroll/brightrolltest/params/race/banner.json +++ b/adapters/brightroll/brightrolltest/params/race/banner.json @@ -1,3 +1,3 @@ { - "publisher": "cafemom" + "publisher": "adthrive" } diff --git a/adapters/brightroll/brightrolltest/params/race/video.json b/adapters/brightroll/brightrolltest/params/race/video.json index 91517e36fd8..6eb4ec6a337 100644 --- a/adapters/brightroll/brightrolltest/params/race/video.json +++ b/adapters/brightroll/brightrolltest/params/race/video.json @@ -1,3 +1,3 @@ { - "publisher": "cafemom" + "publisher": "adthrive" } diff --git a/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json b/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json index a6362c40adb..01beec712c7 100644 --- a/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json +++ b/adapters/brightroll/brightrolltest/supplemental/invalid-imp.json @@ -3,7 +3,7 @@ "id": "test-request-id", "ext": { "bidder": { - "publisher": "cafemom" + "publisher": "adthrive" } } }, diff --git a/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json b/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json new file mode 100644 index 00000000000..da48108af0b --- /dev/null +++ b/adapters/brightroll/brightrolltest/supplemental/invalid-publisher.json @@ -0,0 +1,34 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-missing-req-param-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "publisher":"test" + } + } + + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Invalid publisher", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index d056de664b7..a160e87aad7 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -117,7 +117,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAvocet: avocet.NewAvocetAdapter(cfg.Adapters[string(openrtb_ext.BidderAvocet)].Endpoint), openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo), openrtb_ext.BidderBeintoo: beintoo.NewBeintooBidder(cfg.Adapters[string(openrtb_ext.BidderBeintoo)].Endpoint), - openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), + openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBrightroll)].ExtraAdapterInfo), openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), From 480d2a22042597a3179c3504b39e54d7a715fe00 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Tue, 8 Sep 2020 17:14:52 -0400 Subject: [PATCH 192/318] Refactor TCF 1/2 Vendor List Fetcher Tests (#1441) --- gdpr/gdpr.go | 9 +- gdpr/impl_test.go | 183 +++--- gdpr/vendorlist-fetching.go | 118 ++-- gdpr/vendorlist-fetching_test.go | 954 ++++++++++++++++++++++--------- 4 files changed, 851 insertions(+), 413 deletions(-) diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index 04db8cb92ed..6d447beb438 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -29,9 +29,10 @@ type Permissions interface { AMPException() bool } +// Versions of the GDPR TCF technical specification. const ( - tCF1 uint8 = 1 - tCF2 uint8 = 2 + tcf1SpecVersion uint8 = 1 + tcf2SpecVersion uint8 = 2 ) // NewPermissions gets an instance of the Permissions for use elsewhere in the project. @@ -45,8 +46,8 @@ func NewPermissions(ctx context.Context, cfg config.GDPR, vendorIDs map[openrtb_ cfg: cfg, vendorIDs: vendorIDs, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tCF1), - tCF2: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tCF2)}, + tcf1SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf1SpecVersion), + tcf2SpecVersion: newVendorListFetcher(ctx, cfg, client, vendorListURLMaker, tcf2SpecVersion)}, } } diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index 053e87536ab..d5114454f06 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -23,8 +23,8 @@ func TestNoConsentButAllowByDefault(t *testing.T) { }, vendorIDs: nil, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: failedListFetcher, - tCF2: failedListFetcher, + tcf1SpecVersion: failedListFetcher, + tcf2SpecVersion: failedListFetcher, }, } allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") @@ -43,8 +43,8 @@ func TestNoConsentAndRejectByDefault(t *testing.T) { }, vendorIDs: nil, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: failedListFetcher, - tCF2: failedListFetcher, + tcf1SpecVersion: failedListFetcher, + tcf2SpecVersion: failedListFetcher, }, } allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") @@ -56,12 +56,11 @@ func TestNoConsentAndRejectByDefault(t *testing.T) { } func TestAllowedSyncs(t *testing.T) { - vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ - 2: { - purposes: []int{1}, - }, - 3: { - purposes: []int{1}, + vendorListData := tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{ + {ID: 2, Purposes: []int{1}}, + {ID: 3, Purposes: []int{1}}, }, }) perms := permissionsImpl{ @@ -73,10 +72,10 @@ func TestAllowedSyncs(t *testing.T) { openrtb_ext.BidderPubmatic: 3, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), }, @@ -92,12 +91,11 @@ func TestAllowedSyncs(t *testing.T) { } func TestProhibitedPurposes(t *testing.T) { - vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ - 2: { - purposes: []int{1}, // cookie reads/writes - }, - 3: { - purposes: []int{3}, // ad personalization + vendorListData := tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{ + {ID: 2, Purposes: []int{1}}, // cookie reads/writes + {ID: 3, Purposes: []int{3}}, // ad personalization }, }) perms := permissionsImpl{ @@ -109,10 +107,10 @@ func TestProhibitedPurposes(t *testing.T) { openrtb_ext.BidderPubmatic: 3, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), }, @@ -128,12 +126,11 @@ func TestProhibitedPurposes(t *testing.T) { } func TestProhibitedVendors(t *testing.T) { - vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ - 2: { - purposes: []int{1}, // cookie reads/writes - }, - 3: { - purposes: []int{3}, // ad personalization + vendorListData := tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{ + {ID: 2, Purposes: []int{1}}, // cookie reads/writes + {ID: 3, Purposes: []int{3}}, // ad personalization }, }) perms := permissionsImpl{ @@ -145,10 +142,10 @@ func TestProhibitedVendors(t *testing.T) { openrtb_ext.BidderPubmatic: 3, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), }, @@ -169,8 +166,8 @@ func TestMalformedConsent(t *testing.T) { HostVendorID: 2, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: listFetcher(nil), - tCF2: listFetcher(nil), + tcf1SpecVersion: listFetcher(nil), + tcf2SpecVersion: listFetcher(nil), }, } @@ -180,12 +177,11 @@ func TestMalformedConsent(t *testing.T) { } func TestAllowPersonalInfo(t *testing.T) { - vendorListData := mockVendorListData(t, 1, map[uint16]*purposes{ - 2: { - purposes: []int{1}, // cookie reads/writes - }, - 3: { - purposes: []int{1, 3}, // ad personalization + vendorListData := tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{ + {ID: 2, Purposes: []int{1}}, // cookie reads/writes + {ID: 3, Purposes: []int{1, 3}}, // ad personalization }, }) perms := permissionsImpl{ @@ -197,10 +193,10 @@ func TestAllowPersonalInfo(t *testing.T) { openrtb_ext.BidderPubmatic: 3, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 1: parseVendorListData(t, vendorListData), }), }, @@ -222,24 +218,39 @@ func TestAllowPersonalInfo(t *testing.T) { assertBoolsEqual(t, true, allowPI) } -var tcf2BasicPurposes = map[uint16]*purposes{ - 2: {purposes: []int{1}}, //cookie reads/writes - 6: {purposes: []int{1, 2, 4}}, // ad personalization - 8: {purposes: []int{1, 7}}, - 10: {purposes: []int{2, 4, 7}}, - 32: {purposes: []int{1, 2, 4, 7}}, -} -var tcf2LegitInterests = map[uint16]*purposes{ - 6: {purposes: []int{7}}, - 8: {purposes: []int{2, 4}}, -} -var tcf2SpecialPuproses = map[uint16]*purposes{ - 6: {purposes: []int{1}}, - 10: {purposes: []int{1}}, -} -var tcf2FlexPurposes = map[uint16]*purposes{ - 6: {purposes: []int{1, 2, 4, 7}}, +func buildTCF2VendorList34() tcf2VendorList { + return tcf2VendorList{ + VendorListVersion: 2, + Vendors: map[string]*tcf2Vendor{ + "2": { + ID: 2, + Purposes: []int{1}, + }, + "6": { + ID: 6, + Purposes: []int{1, 2, 4}, + LegIntPurposes: []int{7}, + SpecialPurposes: []int{1}, + FlexiblePurposes: []int{1, 2, 4, 7}, + }, + "8": { + ID: 8, + Purposes: []int{1, 7}, + LegIntPurposes: []int{2, 4}, + }, + "10": { + ID: 10, + Purposes: []int{2, 4, 7}, + SpecialPurposes: []int{1}, + }, + "32": { + ID: 32, + Purposes: []int{1, 2, 4, 7}, + }, + }, + } } + var tcf2Config = config.GDPR{ HostVendorID: 2, TCF2: config.TCF2{ @@ -261,7 +272,7 @@ type tcf2TestDef struct { } func TestAllowPersonalInfoTCF2(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -270,8 +281,8 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, @@ -316,7 +327,7 @@ func TestAllowPersonalInfoTCF2(t *testing.T) { } func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -325,8 +336,8 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, @@ -338,11 +349,10 @@ func TestAllowPersonalInfoWhitelistTCF2(t *testing.T) { assert.EqualValuesf(t, true, allowPI, "AllowPI failure") assert.EqualValuesf(t, true, allowGeo, "AllowGeo failure") assert.EqualValuesf(t, true, allowID, "AllowID failure") - } func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -351,8 +361,8 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 15: parseVendorListDataV2(t, vendorListData), }), }, @@ -397,7 +407,7 @@ func TestAllowPersonalInfoTCF2PubRestrict(t *testing.T) { } func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -406,8 +416,8 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, @@ -453,7 +463,7 @@ func TestAllowPersonalInfoTCF2PurposeOneTrue(t *testing.T) { } func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -462,8 +472,8 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, @@ -510,7 +520,7 @@ func TestAllowPersonalInfoTCF2PurposeOneFalse(t *testing.T) { } func TestAllowSyncTCF2(t *testing.T) { - vendorListData := mockVendorListDataTCF2(t, 2, tcf2BasicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -519,8 +529,8 @@ func TestAllowSyncTCF2(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, @@ -537,9 +547,9 @@ func TestAllowSyncTCF2(t *testing.T) { } func TestProhibitedPurposeSyncTCF2(t *testing.T) { - basicPurposes := tcf2BasicPurposes - basicPurposes[8] = &purposes{purposes: []int{7}} - vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + tcf2VendorList34 := buildTCF2VendorList34() + tcf2VendorList34.Vendors["8"].Purposes = []int{7} + vendorListData := tcf2MarshalVendorList(tcf2VendorList34) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -548,15 +558,15 @@ func TestProhibitedPurposeSyncTCF2(t *testing.T) { openrtb_ext.BidderRubicon: 8, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, } perms.cfg.HostVendorID = 8 - // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8 allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") @@ -567,9 +577,7 @@ func TestProhibitedPurposeSyncTCF2(t *testing.T) { } func TestProhibitedVendorSyncTCF2(t *testing.T) { - basicPurposes := tcf2BasicPurposes - basicPurposes[10] = &purposes{purposes: []int{1}} - vendorListData := mockVendorListDataTCF2(t, 2, basicPurposes, tcf2LegitInterests, tcf2FlexPurposes, tcf2SpecialPuproses) + vendorListData := tcf2MarshalVendorList(buildTCF2VendorList34()) perms := permissionsImpl{ cfg: tcf2Config, vendorIDs: map[openrtb_ext.BidderName]uint16{ @@ -579,20 +587,21 @@ func TestProhibitedVendorSyncTCF2(t *testing.T) { openrtb_ext.BidderOpenx: 10, }, fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tCF1: nil, - tCF2: listFetcher(map[uint16]vendorlist.VendorList{ + tcf1SpecVersion: nil, + tcf2SpecVersion: listFetcher(map[uint16]vendorlist.VendorList{ 34: parseVendorListDataV2(t, vendorListData), }), }, } perms.cfg.HostVendorID = 10 - // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 4, 6 + // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8 allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + // Permission disallowed due to consent string not including vendor 10. + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderOpenx, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure") } diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index 1442f81c3ba..66a3f4ad2d6 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -26,67 +26,83 @@ type saveVendors func(uint16, api.VendorList) // // Nothing in this file is exported. Public APIs can be found in gdpr.go -func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, TCFVer uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { - var fallbackVL api.VendorList = nil - - if TCFVer == tCF1 && len(cfg.TCF1.FallbackGVLPath) > 0 { - fallbackVL = loadFallbackGVL(cfg.TCF1.FallbackGVLPath) - } - - // If we are not going to try fetching the GVL dynamically, we have a simple fetcher - if !cfg.TCF1.FetchGVL && TCFVer == tCF1 && fallbackVL != nil { - return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { - return fallbackVL, nil +func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, tcfSpecVersion uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { + var fallback api.VendorList + if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) > 0 { + fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath) + } + + // If we are not going to try fetching the GVL dynamically, we have a simple fetcher. + if !cfg.TCF1.FetchGVL && tcfSpecVersion == tcf1SpecVersion { + if fallback != nil { + return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { + return fallback, nil + } + } + return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { + return nil, makeVendorListNotFoundError(vendorListVersion) } } - // These save and load functions can be used to store & retrieve lists from our cache. - save, load := newVendorListCache(fallbackVL) - withTimeout, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout()) - defer cancel() - populateCache(withTimeout, client, urlMaker, save, TCFVer) + cacheSave, cacheLoad := newVendorListCache(fallback) - saveOneSometimes := newOccasionalSaver(cfg.Timeouts.ActiveTimeout(), TCFVer) + preloadContext, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout()) + defer cancel() + preloadCache(preloadContext, client, urlMaker, cacheSave, tcfSpecVersion) - return func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { - list := load(id) - if list != nil { + saveOneRateLimited := newOccasionalSaver(cfg.Timeouts.ActiveTimeout(), tcfSpecVersion) + return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { + // Attempt To Load From Cache + if list := cacheLoad(vendorListVersion); list != nil { return list, nil } - saveOneSometimes(ctx, client, urlMaker(id, TCFVer), save) - list = load(id) - if list != nil { + + // Attempt To Download + // - May not add to cache immediately. + saveOneRateLimited(ctx, client, urlMaker(vendorListVersion, tcfSpecVersion), cacheSave) + + // Attempt To Load From Cache Again + // - May have been added by the call to saveOneRateLimited. + if list := cacheLoad(vendorListVersion); list != nil { return list, nil } - if fallbackVL != nil { - return fallbackVL, nil + + // Attempt To Use Hardcoded Fallback + if fallback != nil { + return fallback, nil } - return nil, fmt.Errorf("gdpr vendor list version %d does not exist, or has not been loaded yet. Try again in a few minutes", id) + + // Give Up + return nil, makeVendorListNotFoundError(vendorListVersion) } } -// populateCache saves all the known versions of the vendor list for future use. -func populateCache(ctx context.Context, client *http.Client, urlMaker func(uint16, uint8) string, saver saveVendors, TCFVer uint8) { - latestVersion := saveOne(ctx, client, urlMaker(0, TCFVer), saver, TCFVer) +func makeVendorListNotFoundError(vendorListVersion uint16) error { + return fmt.Errorf("gdpr vendor list version %d does not exist, or has not been loaded yet. Try again in a few minutes", vendorListVersion) +} + +// preloadCache saves all the known versions of the vendor list for future use. +func preloadCache(ctx context.Context, client *http.Client, urlMaker func(uint16, uint8) string, saver saveVendors, tcfSpecVersion uint8) { + latestVersion := saveOne(ctx, client, urlMaker(0, tcfSpecVersion), saver, tcfSpecVersion) for i := uint16(1); i < latestVersion; i++ { - saveOne(ctx, client, urlMaker(i, TCFVer), saver, TCFVer) + saveOne(ctx, client, urlMaker(i, tcfSpecVersion), saver, tcfSpecVersion) } } // Make a URL which can be used to fetch a given version of the Global Vendor List. If the version is 0, // this will fetch the latest version. -func vendorListURLMaker(version uint16, TCFVer uint8) string { - if TCFVer == 2 { - if version == 0 { +func vendorListURLMaker(vendorListVersion uint16, tcfSpecVersion uint8) string { + if tcfSpecVersion == tcf2SpecVersion { + if vendorListVersion == 0 { return "https://vendorlist.consensu.org/v2/vendor-list.json" } - return "https://vendorlist.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(version)) + ".json" + return "https://vendorlist.consensu.org/v2/archives/vendor-list-v" + strconv.Itoa(int(vendorListVersion)) + ".json" } - if version == 0 { + if vendorListVersion == 0 { return "https://vendorlist.consensu.org/vendorlist.json" } - return "https://vendorlist.consensu.org/v-" + strconv.Itoa(int(version)) + "/vendorlist.json" + return "https://vendorlist.consensu.org/v-" + strconv.Itoa(int(vendorListVersion)) + "/vendorlist.json" } // newOccasionalSaver returns a wrapped version of saveOne() which only activates every few minutes. @@ -94,22 +110,24 @@ func vendorListURLMaker(version uint16, TCFVer uint8) string { // The goal here is to update quickly when new versions of the VendorList are released, but not wreck // server performance if a bad CMP starts sending us malformed consent strings that advertize a version // that doesn't exist yet. -func newOccasionalSaver(timeout time.Duration, TCFVer uint8) func(ctx context.Context, client *http.Client, url string, saver saveVendors) { +func newOccasionalSaver(timeout time.Duration, tcfSpecVersion uint8) func(ctx context.Context, client *http.Client, url string, saver saveVendors) { lastSaved := &atomic.Value{} lastSaved.Store(time.Time{}) return func(ctx context.Context, client *http.Client, url string, saver saveVendors) { now := time.Now() - if now.Sub(lastSaved.Load().(time.Time)).Minutes() > 10 { + timeSinceLastSave := now.Sub(lastSaved.Load().(time.Time)) + + if timeSinceLastSave.Minutes() > 10 { withTimeout, cancel := context.WithTimeout(ctx, timeout) defer cancel() - saveOne(withTimeout, client, url, saver, TCFVer) + saveOne(withTimeout, client, url, saver, tcfSpecVersion) lastSaved.Store(now) } } } -func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors, cTFVer uint8) uint16 { +func saveOne(ctx context.Context, client *http.Client, url string, saver saveVendors, tcfSpecVersion uint8) uint16 { req, err := http.NewRequest("GET", url, nil) if err != nil { glog.Errorf("Failed to build GET %s request. Cookie syncs may be affected: %v", url, err) @@ -133,7 +151,7 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen return 0 } var newList api.VendorList - if cTFVer == 2 { + if tcfSpecVersion == tcf2SpecVersion { newList, err = vendorlist2.ParseEagerly(respBody) } else { newList, err = vendorlist.ParseEagerly(respBody) @@ -147,14 +165,15 @@ func saveOne(ctx context.Context, client *http.Client, url string, saver saveVen return newList.Version() } -func newVendorListCache(fallbackVL api.VendorList) (save func(id uint16, list api.VendorList), load func(id uint16) api.VendorList) { +func newVendorListCache(fallbackVL api.VendorList) (save func(vendorListVersion uint16, list api.VendorList), load func(vendorListVersion uint16) api.VendorList) { cache := &sync.Map{} - save = func(id uint16, list api.VendorList) { - cache.Store(id, list) + save = func(vendorListVersion uint16, list api.VendorList) { + cache.Store(vendorListVersion, list) } - load = func(id uint16) api.VendorList { - list, ok := cache.Load(id) + + load = func(vendorListVersion uint16) api.VendorList { + list, ok := cache.Load(vendorListVersion) if ok { return list.(vendorlist.VendorList) } @@ -164,13 +183,14 @@ func newVendorListCache(fallbackVL api.VendorList) (save func(id uint16, list ap } func loadFallbackGVL(fallbackGVLPath string) vendorlist.VendorList { - fallbackVLbody, err := ioutil.ReadFile(fallbackGVLPath) + fallbackContents, err := ioutil.ReadFile(fallbackGVLPath) if err != nil { glog.Fatalf("Error reading from file %s: %v", fallbackGVLPath, err) } - fallbackVL, err := vendorlist.ParseEagerly(fallbackVLbody) + + fallback, err := vendorlist.ParseEagerly(fallbackContents) if err != nil { glog.Fatalf("Error processing default GVL from %s: %v", fallbackGVLPath, err) } - return fallbackVL + return fallback } diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 484a0a54b41..e5ad8793b4f 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -7,233 +7,697 @@ import ( "net/http/httptest" "strconv" "testing" - "time" "github.com/stretchr/testify/assert" + "github.com/prebid/go-gdpr/consentconstants" "github.com/prebid/prebid-server/config" ) -func TestVendorFetch(t *testing.T) { - vendorListOne := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, - }, - }) - vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2, 3}, +func TestTCF1FetcherInitialLoad(t *testing.T) { + // Loads two vendor lists during initialization by setting the latest vendor list version to 2. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 2, + vendorLists: map[int]string{ + 1: tcf1VendorList1, + 2: tcf1VendorList2, }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ - 1: vendorListOne, - 2: vendorListTwo, }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) - list, err := fetcher(context.Background(), 1) - assertNilErr(t, err) - vendor := list.Vendor(32) - assertBoolsEqual(t, true, vendor.Purpose(1)) - assertBoolsEqual(t, false, vendor.Purpose(3)) - assertBoolsEqual(t, false, vendor.Purpose(4)) - - list, err = fetcher(context.Background(), 2) - assertNilErr(t, err) - vendor = list.Vendor(32) - assertBoolsEqual(t, true, vendor.Purpose(1)) - assertBoolsEqual(t, true, vendor.Purpose(3)) -} - -func TestLazyFetch(t *testing.T) { - firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, - }, - }) - secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ - 3: { - purposes: []int{1}, - }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ - 1: firstVendorList, - 2: secondVendorList, + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "Fetch - Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 1, + }, + expected: vendorListFallbackExpected, + }, + { + description: "No Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorListFallbackExpected, + }, + { + description: "No Fetch - No Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 1, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + { + description: "No Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + } + + for _, test := range testCases { + runTest(t, test, tcf1SpecVersion, server) + } +} + +func TestTCF2FetcherInitialLoad(t *testing.T) { + // Loads two vendor lists during initialization by setting the latest vendor list version to 2. + // Ensures TCF1 fetch settings have no effect on TCF2. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 2, + vendorLists: map[int]string{ + 1: tcf2VendorList1, + 2: tcf2VendorList2, + }, }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) - list, err := fetcher(context.Background(), 2) - assertNilErr(t, err) + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "Fetch - Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "No Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - No Fallback - Vendor List 1", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 1, + }, + expected: vendorList1Expected, + }, + { + description: "No Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + } - vendor := list.Vendor(3) - assertBoolsEqual(t, true, vendor.Purpose(1)) - assertBoolsEqual(t, false, vendor.Purpose(2)) + for _, test := range testCases { + runTest(t, test, tcf2SpecVersion, server) + } } -func TestInitialTimeout(t *testing.T) { - list := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, +func TestTCF1FetcherDynamicLoadListExists(t *testing.T) { + // Loads the first vendor list during initialization by setting the latest vendor list version to 1. + // All other vendor lists will be dynamically loaded. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf1VendorList1, + 2: tcf1VendorList2, }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ - 1: list, }))) defer server.Close() - ctx, cancel := context.WithDeadline(context.Background(), time.Time{}) - defer cancel() - fetcher := newVendorListFetcher(ctx, testConfig(), server.Client(), testURLMaker(server), 1) - _, err := fetcher(context.Background(), 1) // This should do a lazy fetch, even though the initial call failed - assertNilErr(t, err) + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorListFallbackExpected, + }, + { + description: "No Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + } + + for _, test := range testCases { + runTest(t, test, tcf1SpecVersion, server) + } } -func TestFetchThrottling(t *testing.T) { - vendorListTwo := mockVendorListData(t, 2, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, - }, - }) - vendorListThree := mockVendorListData(t, 3, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, +func TestTCF2FetcherDynamicLoadListExists(t *testing.T) { + // Loads the first vendor list during initialization by setting the latest vendor list version to 1. + // All other vendor lists will be dynamically loaded. + // Ensures TCF1 fetch settings have no effect on TCF2. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf2VendorList1, + 2: tcf2VendorList2, }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ - 1: "{}", - 2: vendorListTwo, - 3: vendorListThree, }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) - _, err := fetcher(context.Background(), 2) - assertNilErr(t, err) - _, err = fetcher(context.Background(), 3) - assertErr(t, err, false) + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + { + description: "No Fetch - No Fallback - Vendor List 2", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: vendorList2Expected, + }, + } + + for _, test := range testCases { + runTest(t, test, tcf2SpecVersion, server) + } } -func TestMalformedVendorlistFetch(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{1: "{}"}))) +func TestTCF1FetcherDynamicLoadListDoesntExist(t *testing.T) { + // Loads the first vendor list during initialization by setting the latest vendor list version to 1. + // All other vendor list load attempts will be done dynamically. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf1VendorList1, + }, + }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) - _, err := fetcher(context.Background(), 1) - assertErr(t, err, false) + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + { + description: "Fetch - Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorListFallbackExpected, + }, + { + description: "No Fetch - Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: vendorListFallbackExpected, + }, + { + description: "No Fetch - No Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + } + + for _, test := range testCases { + runTest(t, test, 1, server) + } } -func TestMissingVendorlistFetch(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{1: "{}"}))) +func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) { + // Loads the first vendor list during initialization by setting the latest vendor list version to 1. + // All other vendor list load attempts will be done dynamically. + // Ensures TCF1 fetch settings have no effect on TCF2. + + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf2VendorList1, + }, + }))) defer server.Close() - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), 1) - _, err := fetcher(context.Background(), 2) - assertErr(t, err, false) -} + testCases := []test{ + { + description: "Fetch - No Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + { + description: "Fetch - Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: true, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + { + description: "No Fetch - Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: true, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + { + description: "No Fetch - No Fallback - Vendor Doesn't Exist", + setup: testSetup{ + enableTCF1Fetch: false, + enableTCF1Fallback: false, + vendorListVersion: 2, + }, + expected: testExpected{ + errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", + }, + }, + } -func TestVendorListMaker(t *testing.T) { - assertStringsEqual(t, "https://vendorlist.consensu.org/vendorlist.json", vendorListURLMaker(0, 1)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v-2/vendorlist.json", vendorListURLMaker(2, 1)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v-12/vendorlist.json", vendorListURLMaker(12, 1)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v2/vendor-list.json", vendorListURLMaker(0, 2)) - assertStringsEqual(t, "https://vendorlist.consensu.org/v2/archives/vendor-list-v7.json", vendorListURLMaker(7, 2)) + for _, test := range testCases { + runTest(t, test, tcf2SpecVersion, server) + } } -func TestDefaultVendorList(t *testing.T) { - firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, - }, - }) - secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ - 12: { - purposes: []int{2}, +func TestTCF1FetcherThrottling(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1}}}, + }), + 2: tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 2, + Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1, 2}}}, + }), + 3: tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 3, + Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1, 2, 3}}}, + }), }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(2, map[int]string{ - 1: firstVendorList, - 2: secondVendorList, }))) defer server.Close() - testcfg := testConfig() - testcfg.TCF1.FetchGVL = true - testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" - fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) - list, err := fetcher(context.Background(), 12) - assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) - assert.Equal(t, uint16(215), list.Version(), "Expected to fetch default version 215, got %d", list.Version()) + // Dynamically Load List 2 Successfully + _, errList1 := fetcher(context.Background(), 2) + assert.NoError(t, errList1) - // Testing that we got the default vendorlist data, and not the version off the server. - vendor := list.Vendor(12) - assert.Equal(t, true, vendor.Purpose(1)) - assert.Equal(t, false, vendor.Purpose(2)) + // Fail To Load List 3 Due To Rate Limiting + // - The request is rate limited after dynamically list 2. + _, errList2 := fetcher(context.Background(), 3) + assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes") } -func TestFallbackVendorListPassthrough(t *testing.T) { - firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, +func TestTCF2FetcherThrottling(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: tcf2MarshalVendorList(tcf2VendorList{ + VendorListVersion: 1, + Vendors: map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{1}}}, + }), + 2: tcf2MarshalVendorList(tcf2VendorList{ + VendorListVersion: 2, + Vendors: map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{1, 2}}}, + }), + 3: tcf2MarshalVendorList(tcf2VendorList{ + VendorListVersion: 3, + Vendors: map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{1, 2, 3}}}, + }), }, - }) - secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ - 12: { - purposes: []int{2}, + }))) + defer server.Close() + + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion) + + // Dynamically Load List 2 Successfully + _, errList1 := fetcher(context.Background(), 2) + assert.NoError(t, errList1) + + // Fail To Load List 3 Due To Rate Limiting + // - The request is rate limited after dynamically list 2. + _, errList2 := fetcher(context.Background(), 3) + assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes") +} + +func TestTCF1MalformedVendorlist(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: "malformed", }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ - 1: firstVendorList, - 2: secondVendorList, }))) defer server.Close() - testcfg := testConfig() - testcfg.TCF1.FetchGVL = true - testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" - fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) - list, err := fetcher(context.Background(), 2) - assert.NoError(t, err, "Error with fetching existing vendorlist: %v", err) - assert.Equal(t, uint16(2), list.Version(), "Expected to fetch mock list version 2, got version %d", list.Version()) - - // Testing that we got the testing vendorlist data, and not the default. - vendor := list.Vendor(12) - assert.Equal(t, false, vendor.Purpose(1)) - assert.Equal(t, true, vendor.Purpose(2)) -} - -func TestFallbackVendorListNoFetch(t *testing.T) { - firstVendorList := mockVendorListData(t, 1, map[uint16]*purposes{ - 32: { - purposes: []int{1, 2}, - }, - }) - secondVendorList := mockVendorListData(t, 2, map[uint16]*purposes{ - 12: { - purposes: []int{2}, - }, - }) - server := httptest.NewServer(http.HandlerFunc(mockServer(1, map[int]string{ - 1: firstVendorList, - 2: secondVendorList, + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) + _, err := fetcher(context.Background(), 1) + + // Fetching should fail since vendor list could not be unmarshalled. + assert.Error(t, err) +} + +func TestTCF2MalformedVendorlist(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ + vendorListLatestVersion: 1, + vendorLists: map[int]string{ + 1: "malformed", + }, }))) defer server.Close() - testcfg := testConfig() - testcfg.TCF1.FetchGVL = false - testcfg.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" - fetcher := newVendorListFetcher(context.Background(), testcfg, server.Client(), testURLMaker(server), 1) - list, err := fetcher(context.Background(), 2) - assert.NoError(t, err, "Error with fetching default vendorlist: %v", err) - assert.Equal(t, uint16(215), list.Version(), "Expected to fetch default version 215, got %d", list.Version()) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion) + _, err := fetcher(context.Background(), 1) + + // Fetching should fail since vendor list could not be unmarshalled. + assert.Error(t, err) +} + +func TestTCF1ServerUrlInvalid(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + server.Close() + + invalidURLGenerator := func(uint16, uint8) string { return " http://invalid-url-has-leading-whitespace" } - // Testing that we got the default vendorlist data, and not the version off the server. - vendor := list.Vendor(12) - assert.Equal(t, true, vendor.Purpose(1)) - assert.Equal(t, false, vendor.Purpose(2)) + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), invalidURLGenerator, tcf1SpecVersion) + _, err := fetcher(context.Background(), 1) + assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") +} + +func TestTCF2ServerUrlInvalid(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + server.Close() + + invalidURLGenerator := func(uint16, uint8) string { return " http://invalid-url-has-leading-whitespace" } + + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), invalidURLGenerator, tcf2SpecVersion) + _, err := fetcher(context.Background(), 1) + + assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") +} + +func TestTCF1ServerUnavailable(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + server.Close() + + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) + _, err := fetcher(context.Background(), 1) + + assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") +} + +func TestTCF2ServerUnavailable(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + server.Close() + + fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf2SpecVersion) + _, err := fetcher(context.Background(), 1) + + assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") +} + +func TestVendorListURLMaker(t *testing.T) { + testCases := []struct { + description string + tcfSpecVersion uint8 + vendorListVersion uint16 + expectedURL string + }{ + { + description: "TCF1 - Latest", + tcfSpecVersion: 1, + vendorListVersion: 0, // Forces latest version. + expectedURL: "https://vendorlist.consensu.org/vendorlist.json", + }, + { + description: "TCF1 - Specific", + tcfSpecVersion: 1, + vendorListVersion: 42, + expectedURL: "https://vendorlist.consensu.org/v-42/vendorlist.json", + }, + { + description: "TCF2 - Latest", + tcfSpecVersion: 2, + vendorListVersion: 0, // Forces latest version. + expectedURL: "https://vendorlist.consensu.org/v2/vendor-list.json", + }, + { + description: "TCF2 - Specific", + tcfSpecVersion: 2, + vendorListVersion: 42, + expectedURL: "https://vendorlist.consensu.org/v2/archives/vendor-list-v42.json", + }, + } + + for _, test := range testCases { + result := vendorListURLMaker(test.vendorListVersion, test.tcfSpecVersion) + assert.Equal(t, test.expectedURL, result) + } +} + +var tcf1VendorList1 = tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 1, + Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{2}}}, +}) + +var tcf2VendorList1 = tcf2MarshalVendorList(tcf2VendorList{ + VendorListVersion: 1, + Vendors: map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{2}}}, +}) + +var vendorList1Expected = testExpected{ + vendorListVersion: 1, + vendorID: 12, + vendorPurposes: map[int]bool{1: false, 2: true, 3: false}, +} + +var tcf1VendorList2 = tcf1MarshalVendorList(tcf1VendorList{ + VendorListVersion: 2, + Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{2, 3}}}, +}) + +var tcf2VendorList2 = tcf2MarshalVendorList(tcf2VendorList{ + VendorListVersion: 2, + Vendors: map[string]*tcf2Vendor{"12": {ID: 12, Purposes: []int{2, 3}}}, +}) + +var vendorList2Expected = testExpected{ + vendorListVersion: 2, + vendorID: 12, + vendorPurposes: map[int]bool{1: false, 2: true, 3: true}, +} + +var vendorListFallbackExpected = testExpected{ + vendorListVersion: 215, // Values from hardcoded fallback file. + vendorID: 12, + vendorPurposes: map[int]bool{1: true, 2: false, 3: true}, +} + +type tcf1VendorList struct { + VendorListVersion uint16 `json:"vendorListVersion"` + Vendors []tcf1Vendor `json:"vendors"` +} + +type tcf1Vendor struct { + ID uint16 `json:"id"` + Purposes []int `json:"purposeIds"` +} + +func tcf1MarshalVendorList(vendorList tcf1VendorList) string { + json, _ := json.Marshal(vendorList) + return string(json) +} + +type tcf2VendorList struct { + VendorListVersion uint16 `json:"vendorListVersion"` + Vendors map[string]*tcf2Vendor `json:"vendors"` +} + +type tcf2Vendor struct { + ID uint16 `json:"id"` + Purposes []int `json:"purposes"` + LegIntPurposes []int `json:"legIntPurposes"` + FlexiblePurposes []int `json:"flexiblePurposes"` + SpecialPurposes []int `json:"specialPurposes"` +} + +func tcf2MarshalVendorList(vendorList tcf2VendorList) string { + json, _ := json.Marshal(vendorList) + return string(json) +} + +type serverSettings struct { + vendorListLatestVersion int + vendorLists map[int]string } // mockServer returns a handler which returns the given response for each global vendor list version. @@ -247,129 +711,74 @@ func TestFallbackVendorListNoFetch(t *testing.T) { // // If the "version" query param points to a version which doesn't exist, it returns a 403. // Don't ask why... that's just what the official page is doing. See https://vendorlist.consensu.org/v-9999/vendorlist.json -func mockServer(latestVersion int, responses map[int]string) func(http.ResponseWriter, *http.Request) { +func mockServer(settings serverSettings) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, req *http.Request) { - version := req.URL.Query().Get("version") - versionInt, err := strconv.Atoi(version) + vendorListVersion := req.URL.Query().Get("version") + vendorListVersionInt, err := strconv.Atoi(vendorListVersion) if err != nil { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("Request had invalid version: " + version)) + w.Write([]byte("Request had invalid version: " + vendorListVersion)) return } - if versionInt == 0 { - versionInt = latestVersion + if vendorListVersionInt == 0 { + vendorListVersionInt = settings.vendorListLatestVersion } - response, ok := responses[versionInt] + response, ok := settings.vendorLists[vendorListVersionInt] if !ok { w.WriteHeader(http.StatusForbidden) - w.Write([]byte("Version not found: " + version)) + w.Write([]byte("Version not found: " + vendorListVersion)) return } w.Write([]byte(response)) } } -func mockVendorListData(t *testing.T, version uint16, vendors map[uint16]*purposes) string { - type vendorContract struct { - ID uint16 `json:"id"` - Purposes []int `json:"purposeIds"` - } - - type vendorListContract struct { - Version uint16 `json:"vendorListVersion"` - Vendors []vendorContract `json:"vendors"` - } - - buildVendors := func(input map[uint16]*purposes) []vendorContract { - vendors := make([]vendorContract, 0, len(input)) - for id, purpose := range input { - vendors = append(vendors, vendorContract{ - ID: id, - Purposes: purpose.purposes, - }) - } - return vendors - } - - obj := vendorListContract{ - Version: version, - Vendors: buildVendors(vendors), - } - data, err := json.Marshal(obj) - assertNilErr(t, err) - return string(data) +type test struct { + description string + setup testSetup + expected testExpected } -type purposeMap map[uint16]*purposes - -func mockVendorListDataTCF2(t *testing.T, version uint16, basicPurposes purposeMap, legitInterests purposeMap, flexPurposes purposeMap, specialPurposes purposeMap) string { - type vendorContract struct { - ID uint16 `json:"id"` - Purposes []int `json:"purposes"` - LegIntPurposes []int `json:"legIntPurposes"` - FlexiblePurposes []int `json:"flexiblePurposes"` - SpecialPurposes []int `json:"specialPurposes"` - } - - type vendorListContract struct { - Version uint16 `json:"vendorListVersion"` - Vendors map[string]vendorContract `json:"vendors"` - } - - vendors := make(map[string]vendorContract, len(basicPurposes)) - for id, purpose := range basicPurposes { - sid := strconv.Itoa(int(id)) - vendor, ok := vendors[sid] - if !ok { - vendor = vendorContract{ID: id} - } - vendor.Purposes = purpose.purposes - vendors[sid] = vendor - } +type testSetup struct { + enableTCF1Fetch bool + enableTCF1Fallback bool + vendorListVersion uint16 +} - for id, purpose := range legitInterests { - sid := strconv.Itoa(int(id)) - vendor, ok := vendors[sid] - if !ok { - vendor = vendorContract{ID: id} - } - vendor.LegIntPurposes = purpose.purposes - vendors[sid] = vendor - } +type testExpected struct { + errorMessage string + vendorListVersion uint16 + vendorID uint16 + vendorPurposes map[int]bool +} - for id, purpose := range flexPurposes { - sid := strconv.Itoa(int(id)) - vendor, ok := vendors[sid] - if !ok { - vendor = vendorContract{ID: id} - } - vendor.FlexiblePurposes = purpose.purposes - vendors[sid] = vendor +func runTest(t *testing.T, test test, tcfSpecVersion uint8, server *httptest.Server) { + config := testConfig() + config.TCF1.FetchGVL = test.setup.enableTCF1Fetch + if test.setup.enableTCF1Fallback { + config.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" } - for id, purpose := range specialPurposes { - sid := strconv.Itoa(int(id)) - vendor, ok := vendors[sid] - if !ok { - vendor = vendorContract{ID: id} + fetcher := newVendorListFetcher(context.Background(), config, server.Client(), testURLMaker(server), tcfSpecVersion) + vendorList, err := fetcher(context.Background(), test.setup.vendorListVersion) + + if test.expected.errorMessage != "" { + assert.EqualError(t, err, test.expected.errorMessage, test.description+":error") + } else { + assert.NoError(t, err, test.description+":vendorlist") + assert.Equal(t, test.expected.vendorListVersion, vendorList.Version(), test.description+":vendorlistid") + vendor := vendorList.Vendor(test.expected.vendorID) + for id, expected := range test.expected.vendorPurposes { + result := vendor.Purpose(consentconstants.Purpose(id)) + assert.Equalf(t, expected, result, "%s:vendor-%d:purpose-%d", test.description, vendorList.Version(), id) } - vendor.SpecialPurposes = purpose.purposes - vendors[sid] = vendor } - - obj := vendorListContract{ - Version: version, - Vendors: vendors, - } - data, err := json.Marshal(obj) - assertNilErr(t, err) - return string(data) } func testURLMaker(server *httptest.Server) func(uint16, uint8) string { url := server.URL - return func(version uint16, TCFVer uint8) string { - return url + "?version=" + strconv.Itoa(int(version)) + return func(vendorListVersion uint16, tcfSpecVersion uint8) string { + return url + "?version=" + strconv.Itoa(int(vendorListVersion)) } } @@ -379,9 +788,8 @@ func testConfig() config.GDPR { InitVendorlistFetch: 60 * 1000, ActiveVendorlistFetch: 1000 * 5, }, + TCF1: config.TCF1{ + FetchGVL: true, + }, } } - -type purposes struct { - purposes []int -} From 420da24edd3a6316e669ae969dcba10edb57fcaa Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 9 Sep 2020 12:01:33 -0700 Subject: [PATCH 193/318] Add validation checker for PRs and merges with github actions (#1476) --- .github/workflows/validate.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/validate.yml diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 00000000000..d7bb50fbabf --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,28 @@ +on: + push: + branches: + - master + pull_request: + release: + types: + - created +name: Validate +jobs: + Go: + strategy: + matrix: + go-version: [1.13.x, 1.14.x, 1.15.x] + os: [ubuntu-18.04] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + - name: Validate + run: | + ./validate.sh --nofmt --cov --race 10 + env: + GO111MODULE: "on" From 22c454c9e1d2617b109b1577c172a82da6a1358e Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Thu, 10 Sep 2020 10:17:40 -0700 Subject: [PATCH 194/318] Cache refactor (#1431) Reason: Cache has Fetcher-like functionality to handle both requests and imps at a time. Internally, it still uses two caches configured and searched separately, causing some code repetition. Reusing this code to cache other objects like accounts is not easy. Keeping the req/imp repetition in fetcher and out of cache allows for a reusable simpler cache, preserving existing fetcher functionality. Changes in this set: Cache is now a simple generic id->RawMessage store fetcherWithCache handles the separate req and imp caches ComposedCache handles single caches - but it does not appear to be used Removed cache overlap tests since they do not apply now Slightly less code --- stored_requests/caches/cachestest/reliable.go | 65 ++---------- stored_requests/caches/memory/cache.go | 60 ++++------- stored_requests/caches/memory/cache_test.go | 44 +++------ stored_requests/caches/nil_cache/nil_cache.go | 9 +- stored_requests/config/config.go | 7 +- stored_requests/config/config_test.go | 8 +- stored_requests/events/api/api_test.go | 31 +++--- stored_requests/events/events.go | 6 +- stored_requests/events/events_test.go | 21 ++-- stored_requests/fetcher.go | 57 ++++++----- stored_requests/fetcher_test.go | 99 ++++++++++--------- 11 files changed, 169 insertions(+), 238 deletions(-) diff --git a/stored_requests/caches/cachestest/reliable.go b/stored_requests/caches/cachestest/reliable.go index e08a20e9cdb..7fbaf7238af 100644 --- a/stored_requests/caches/cachestest/reliable.go +++ b/stored_requests/caches/cachestest/reliable.go @@ -11,8 +11,6 @@ import ( const ( reqCacheKey = "known-req" reqCacheVal = `{"req":true}` - impCacheKey = "known-imp" - impCacheVal = `{"imp":true}` ) // AssertCacheRobustness runs tests which can be used to validate any Cache that is 100% reliable. @@ -20,84 +18,41 @@ const ( // // The cacheSupplier should be a function which returns a new Cache (with no data inside) on every call. // This will be called from separate Goroutines to make sure that different tests don't conflict. -func AssertCacheRobustness(t *testing.T, cacheSupplier func() stored_requests.Cache) { +func AssertCacheRobustness(t *testing.T, cacheSupplier func() stored_requests.CacheJSON) { t.Run("TestCacheMiss", cacheMissTester(cacheSupplier())) t.Run("TestCacheHit", cacheHitTester(cacheSupplier())) - t.Run("TestCacheMixed", cacheMixedTester(cacheSupplier())) - t.Run("TestCacheOverlap", cacheOverlapTester(cacheSupplier())) t.Run("TestCacheSaveInvalidate", cacheSaveInvalidateTester(cacheSupplier())) } -func cacheMissTester(cache stored_requests.Cache) func(*testing.T) { +func cacheMissTester(cache stored_requests.CacheJSON) func(*testing.T) { return func(t *testing.T) { - storedReqs, storedImps := cache.Get(context.Background(), []string{"unknown"}, nil) - assertMapLength(t, 0, storedReqs) - assertMapLength(t, 0, storedImps) + storedData := cache.Get(context.Background(), []string{"unknown"}) + assertMapLength(t, 0, storedData) } } -func cacheHitTester(cache stored_requests.Cache) func(*testing.T) { +func cacheHitTester(cache stored_requests.CacheJSON) func(*testing.T) { return func(t *testing.T) { cache.Save(context.Background(), map[string]json.RawMessage{ reqCacheKey: json.RawMessage(reqCacheVal), - }, map[string]json.RawMessage{ - impCacheKey: json.RawMessage(impCacheVal), }) - reqData, impData := cache.Get(context.Background(), []string{reqCacheKey}, []string{impCacheKey}) - if len(reqData) != 1 { - t.Errorf("The cache should have returned the data.") - } + reqData := cache.Get(context.Background(), []string{reqCacheKey}) assertMapLength(t, 1, reqData) assertHasValue(t, reqData, reqCacheKey, reqCacheVal) - - assertMapLength(t, 1, impData) - assertHasValue(t, impData, impCacheKey, impCacheVal) - } -} - -func cacheMixedTester(cache stored_requests.Cache) func(*testing.T) { - return func(t *testing.T) { - cache.Save(context.Background(), map[string]json.RawMessage{ - reqCacheKey: json.RawMessage(reqCacheVal), - }, nil) - reqData, impData := cache.Get(context.Background(), []string{reqCacheKey, "unknown-req"}, nil) - assertMapLength(t, 1, reqData) - assertHasValue(t, reqData, reqCacheKey, reqCacheVal) - assertMapLength(t, 0, impData) } } -func cacheOverlapTester(cache stored_requests.Cache) func(*testing.T) { - commonKey := "id" +func cacheSaveInvalidateTester(cache stored_requests.CacheJSON) func(*testing.T) { return func(t *testing.T) { cache.Save(context.Background(), map[string]json.RawMessage{ - commonKey: json.RawMessage(reqCacheVal), - }, map[string]json.RawMessage{ - commonKey: json.RawMessage(impCacheVal), - }) - reqData, impData := cache.Get(context.Background(), []string{commonKey}, []string{commonKey}) - assertMapLength(t, 1, reqData) - assertHasValue(t, reqData, commonKey, reqCacheVal) - assertMapLength(t, 1, impData) - assertHasValue(t, impData, commonKey, impCacheVal) - } -} - -func cacheSaveInvalidateTester(cache stored_requests.Cache) func(*testing.T) { - return func(t *testing.T) { - cache.Save(context.Background(), map[string]json.RawMessage{ - reqCacheKey: json.RawMessage(reqCacheVal), - }, map[string]json.RawMessage{ reqCacheKey: json.RawMessage(reqCacheVal), }) - reqData, impData := cache.Get(context.Background(), []string{reqCacheKey}, []string{reqCacheKey}) + reqData := cache.Get(context.Background(), []string{reqCacheKey}) assertMapLength(t, 1, reqData) - assertMapLength(t, 1, impData) - cache.Invalidate(context.Background(), []string{reqCacheKey}, []string{reqCacheKey}) - reqData, impData = cache.Get(context.Background(), []string{reqCacheKey}, []string{reqCacheKey}) + cache.Invalidate(context.Background(), []string{reqCacheKey}) + reqData = cache.Get(context.Background(), []string{reqCacheKey}) assertMapLength(t, 0, reqData) - assertMapLength(t, 0, impData) } } diff --git a/stored_requests/caches/memory/cache.go b/stored_requests/caches/memory/cache.go index c5702080ef9..aea087e6d19 100644 --- a/stored_requests/caches/memory/cache.go +++ b/stored_requests/caches/memory/cache.go @@ -7,7 +7,6 @@ import ( "github.com/coocood/freecache" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/stored_requests" ) @@ -17,68 +16,51 @@ import ( // 2. The cache is too large. This will cause the least recently used items to be evicted. // // For no TTL, use ttlSeconds <= 0 -func NewCache(cfg *config.InMemoryCache) stored_requests.Cache { - return &cache{ - requestDataCache: newCacheForWithLimits(cfg.RequestCacheSize, cfg.TTL, "Request"), - impDataCache: newCacheForWithLimits(cfg.ImpCacheSize, cfg.TTL, "Imp"), - } -} - -func newCacheForWithLimits(size int, ttl int, dataType string) mapLike { +func NewCache(size int, ttl int, dataType string) stored_requests.CacheJSON { if ttl > 0 && size <= 0 { - glog.Fatal("No in-memory caches defined with a finite TTL but unbounded size. Config validation should have caught this. Failing fast because something is buggy.") + glog.Fatalf("No in-memory %s caches defined with a finite TTL but unbounded size. Config validation should have caught this. Failing fast because something is buggy.", dataType) } if size > 0 { glog.Infof("Using a Stored %s in-memory cache. Max size: %d bytes. TTL: %d seconds.", dataType, size, ttl) - return &pbsLRUCache{ - Cache: freecache.NewCache(size), - ttlSeconds: ttl, + return &cache{ + dataType: dataType, + cache: &pbsLRUCache{ + Cache: freecache.NewCache(size), + ttlSeconds: ttl, + }, } } else { glog.Infof("Using an unbounded Stored %s in-memory cache.", dataType) - return &pbsSyncMap{&sync.Map{}} + return &cache{ + dataType: dataType, + cache: &pbsSyncMap{&sync.Map{}}, + } } } type cache struct { - requestDataCache mapLike - impDataCache mapLike + dataType string + cache mapLike } -func (c *cache) Get(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage) { - requestData = doGet(c.requestDataCache, requestIDs) - impData = doGet(c.impDataCache, impIDs) - return -} - -func doGet(cache mapLike, ids []string) (data map[string]json.RawMessage) { +func (c *cache) Get(ctx context.Context, ids []string) (data map[string]json.RawMessage) { data = make(map[string]json.RawMessage, len(ids)) for _, id := range ids { - if val, ok := cache.Get(id); ok { + if val, ok := c.cache.Get(id); ok { data[id] = val } } return } -func (c *cache) Save(ctx context.Context, storedRequests map[string]json.RawMessage, storedImps map[string]json.RawMessage) { - c.doSave(c.requestDataCache, storedRequests) - c.doSave(c.impDataCache, storedImps) -} - -func (c *cache) doSave(cache mapLike, values map[string]json.RawMessage) { - for id, data := range values { - cache.Set(id, data) +func (c *cache) Save(ctx context.Context, data map[string]json.RawMessage) { + for id, data := range data { + c.cache.Set(id, data) } } -func (c *cache) Invalidate(ctx context.Context, requestIDs []string, impIDs []string) { - doInvalidate(c.requestDataCache, requestIDs) - doInvalidate(c.impDataCache, impIDs) -} - -func doInvalidate(cache mapLike, ids []string) { +func (c *cache) Invalidate(ctx context.Context, ids []string) { for _, id := range ids { - cache.Delete(id) + c.cache.Delete(id) } } diff --git a/stored_requests/caches/memory/cache_test.go b/stored_requests/caches/memory/cache_test.go index ba4703ef89a..b89bd5af26f 100644 --- a/stored_requests/caches/memory/cache_test.go +++ b/stored_requests/caches/memory/cache_test.go @@ -7,52 +7,34 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/caches/cachestest" ) func TestLRURobustness(t *testing.T) { - cachestest.AssertCacheRobustness(t, func() stored_requests.Cache { - return NewCache(&config.InMemoryCache{ - RequestCacheSize: 256 * 1024, - ImpCacheSize: 256 * 1024, - TTL: -1, - }) + cachestest.AssertCacheRobustness(t, func() stored_requests.CacheJSON { + return NewCache(256*1024, -1, "TestData") }) } func TestUnboundedRobustness(t *testing.T) { - cachestest.AssertCacheRobustness(t, func() stored_requests.Cache { - return NewCache(&config.InMemoryCache{ - RequestCacheSize: 0, - ImpCacheSize: 0, - TTL: -1, - }) + cachestest.AssertCacheRobustness(t, func() stored_requests.CacheJSON { + return NewCache(0, -1, "TestData") }) } func TestRaceLRUConcurrency(t *testing.T) { - cache := NewCache(&config.InMemoryCache{ - RequestCacheSize: 256 * 1024, - ImpCacheSize: 256 * 1024, - TTL: -1, - }) - + cache := NewCache(256*1024, -1, "TestData") doRaceTest(t, cache) } func TestRaceUnboundedConcurrency(t *testing.T) { - cache := NewCache(&config.InMemoryCache{ - RequestCacheSize: 0, - ImpCacheSize: 0, - TTL: -1, - }) + cache := NewCache(0, -1, "TestData") doRaceTest(t, cache) } -func doRaceTest(t *testing.T, cache stored_requests.Cache) { +func doRaceTest(t *testing.T, cache stored_requests.CacheJSON) { done := make(chan struct{}) sets := [][]int{rand.Perm(100), rand.Perm(100), rand.Perm(100)} @@ -70,26 +52,26 @@ func doRaceTest(t *testing.T, cache stored_requests.Cache) { } } -func readLots(cache stored_requests.Cache, done chan<- struct{}, reads []int) { +func readLots(cache stored_requests.CacheJSON, done chan<- struct{}, reads []int) { var s struct{} for _, i := range reads { - cache.Get(context.Background(), sliceForVal(i), sliceForVal(-i)) + cache.Get(context.Background(), sliceForVal(i)) } done <- s } -func writeLots(cache stored_requests.Cache, done chan<- struct{}, writes []int) { +func writeLots(cache stored_requests.CacheJSON, done chan<- struct{}, writes []int) { var s struct{} for _, i := range writes { - cache.Save(context.Background(), mapForVal(i), mapForVal(-i)) + cache.Save(context.Background(), mapForVal(i)) } done <- s } -func invalidateLots(cache stored_requests.Cache, done chan<- struct{}, invalidates []int) { +func invalidateLots(cache stored_requests.CacheJSON, done chan<- struct{}, invalidates []int) { var s struct{} for _, i := range invalidates { - cache.Invalidate(context.Background(), sliceForVal(i), sliceForVal(-i)) + cache.Invalidate(context.Background(), sliceForVal(i)) } done <- s } diff --git a/stored_requests/caches/nil_cache/nil_cache.go b/stored_requests/caches/nil_cache/nil_cache.go index de29156e3c9..d043ae55c96 100644 --- a/stored_requests/caches/nil_cache/nil_cache.go +++ b/stored_requests/caches/nil_cache/nil_cache.go @@ -8,13 +8,14 @@ import ( // NilCache is a no-op cache which does nothing useful. type NilCache struct{} -func (c *NilCache) Get(ctx context.Context, requestIDs []string, impIDs []string) (map[string]json.RawMessage, map[string]json.RawMessage) { - return nil, nil +func (c *NilCache) Get(ctx context.Context, ids []string) map[string]json.RawMessage { + return make(map[string]json.RawMessage) } -func (c *NilCache) Save(ctx context.Context, storedRequests map[string]json.RawMessage, storedImps map[string]json.RawMessage) { + +func (c *NilCache) Save(ctx context.Context, data map[string]json.RawMessage) { return } -func (c *NilCache) Invalidate(ctx context.Context, requestIDs []string, impIDs []string) { +func (c *NilCache) Invalidate(ctx context.Context, ids []string) { return } diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go index e81d9667a73..6c663cbf0a2 100644 --- a/stored_requests/config/config.go +++ b/stored_requests/config/config.go @@ -178,10 +178,13 @@ func newFetcher(cfg *config.StoredRequests, client *http.Client, db *sql.DB) (fe func newCache(cfg *config.StoredRequests) stored_requests.Cache { if cfg.InMemoryCache.Type == "none" { glog.Infof("No Stored %s cache configured. The %s Fetcher backend will be used for all data requests", cfg.DataType(), cfg.DataType()) - return &nil_cache.NilCache{} + return stored_requests.Cache{&nil_cache.NilCache{}, &nil_cache.NilCache{}} } - return memory.NewCache(&cfg.InMemoryCache) + return stored_requests.Cache{ + Requests: memory.NewCache(cfg.InMemoryCache.RequestCacheSize, cfg.InMemoryCache.TTL, "Requests"), + Imps: memory.NewCache(cfg.InMemoryCache.ImpCacheSize, cfg.InMemoryCache.TTL, "Imps"), + } } func newEventProducers(cfg *config.StoredRequests, client *http.Client, db *sql.DB, router *httprouter.Router) (eventProducers []events.EventProducer) { diff --git a/stored_requests/config/config_test.go b/stored_requests/config/config_test.go index 4c3943ea5be..af6f4a4f514 100644 --- a/stored_requests/config/config_test.go +++ b/stored_requests/config/config_test.go @@ -102,8 +102,8 @@ func TestNewHTTPEvents(t *testing.T) { func TestNewEmptyCache(t *testing.T) { cache := newCache(&config.StoredRequests{InMemoryCache: config.InMemoryCache{Type: "none"}}) - cache.Save(context.Background(), map[string]json.RawMessage{"foo": json.RawMessage("true")}, nil) - reqs, _ := cache.Get(context.Background(), []string{"foo"}, nil) + cache.Requests.Save(context.Background(), map[string]json.RawMessage{"foo": json.RawMessage("true")}) + reqs := cache.Requests.Get(context.Background(), []string{"foo"}) if len(reqs) != 0 { t.Errorf("The newCache method should return an empty cache if the config asks for it.") } @@ -117,8 +117,8 @@ func TestNewInMemoryCache(t *testing.T) { ImpCacheSize: 100, }, }) - cache.Save(context.Background(), map[string]json.RawMessage{"foo": json.RawMessage("true")}, nil) - reqs, _ := cache.Get(context.Background(), []string{"foo"}, nil) + cache.Requests.Save(context.Background(), map[string]json.RawMessage{"foo": json.RawMessage("true")}) + reqs := cache.Requests.Get(context.Background(), []string{"foo"}) if len(reqs) != 1 { t.Errorf("The newCache method should return an in-memory cache if the config asks for it.") } diff --git a/stored_requests/events/api/api_test.go b/stored_requests/events/api/api_test.go index 64cf68b0a91..d2db4557573 100644 --- a/stored_requests/events/api/api_test.go +++ b/stored_requests/events/api/api_test.go @@ -9,22 +9,21 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/caches/memory" "github.com/prebid/prebid-server/stored_requests/events" ) func TestGoodRequests(t *testing.T) { - cache := memory.NewCache(&config.InMemoryCache{ - RequestCacheSize: 256 * 1024, - ImpCacheSize: 256 * 1024, - TTL: -1, - }) - + cache := stored_requests.Cache{ + Requests: memory.NewCache(256*1024, -1, "Requests"), + Imps: memory.NewCache(256*1024, -1, "Imps"), + } id := "1" config := fmt.Sprintf(`{"id": "%s"}`, id) initialValue := map[string]json.RawMessage{id: json.RawMessage(config)} - cache.Save(context.Background(), initialValue, initialValue) + cache.Requests.Save(context.Background(), initialValue) + cache.Imps.Save(context.Background(), initialValue) apiEvents, endpoint := NewEventsAPI() @@ -51,7 +50,8 @@ func TestGoodRequests(t *testing.T) { } <-updateOccurred - reqData, impData := cache.Get(context.Background(), []string{id}, []string{id}) + reqData := cache.Requests.Get(context.Background(), []string{id}) + impData := cache.Imps.Get(context.Background(), []string{id}) assertHasValue(t, reqData, id, config) assertHasValue(t, impData, id, config) @@ -66,18 +66,17 @@ func TestGoodRequests(t *testing.T) { } <-invalidateOccurred - reqData, impData = cache.Get(context.Background(), []string{id}, []string{id}) + reqData = cache.Requests.Get(context.Background(), []string{id}) + impData = cache.Imps.Get(context.Background(), []string{id}) assertMapLength(t, 0, reqData) assertMapLength(t, 0, impData) } func TestBadRequests(t *testing.T) { - cache := memory.NewCache(&config.InMemoryCache{ - RequestCacheSize: 256 * 1024, - ImpCacheSize: 256 * 1024, - TTL: -1, - }) - + cache := stored_requests.Cache{ + Requests: memory.NewCache(256*1024, -1, "Requests"), + Imps: memory.NewCache(256*1024, -1, "Imps"), + } apiEvents, endpoint := NewEventsAPI() listener := events.SimpleEventListener() go listener.Listen(cache, apiEvents) diff --git a/stored_requests/events/events.go b/stored_requests/events/events.go index ea67e8eeeb9..ba08f13d65b 100644 --- a/stored_requests/events/events.go +++ b/stored_requests/events/events.go @@ -61,12 +61,14 @@ func (e *EventListener) Listen(cache stored_requests.Cache, events EventProducer for { select { case save := <-events.Saves(): - cache.Save(context.Background(), save.Requests, save.Imps) + cache.Requests.Save(context.Background(), save.Requests) + cache.Imps.Save(context.Background(), save.Imps) if e.onSave != nil { e.onSave() } case invalidation := <-events.Invalidations(): - cache.Invalidate(context.Background(), invalidation.Requests, invalidation.Imps) + cache.Requests.Invalidate(context.Background(), invalidation.Requests) + cache.Imps.Invalidate(context.Background(), invalidation.Imps) if e.onInvalidate != nil { e.onInvalidate() } diff --git a/stored_requests/events/events_test.go b/stored_requests/events/events_test.go index 240a697592a..74263d14b93 100644 --- a/stored_requests/events/events_test.go +++ b/stored_requests/events/events_test.go @@ -7,7 +7,7 @@ import ( "reflect" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/caches/memory" ) @@ -16,12 +16,10 @@ func TestListen(t *testing.T) { saves: make(chan Save), invalidations: make(chan Invalidation), } - - cache := memory.NewCache(&config.InMemoryCache{ - RequestCacheSize: 256 * 1024, - ImpCacheSize: 256 * 1024, - TTL: -1, - }) + cache := stored_requests.Cache{ + Requests: memory.NewCache(256*1024, -1, "Requests"), + Imps: memory.NewCache(256*1024, -1, "Imps"), + } // create channels to synchronize saveOccurred := make(chan struct{}) @@ -42,7 +40,8 @@ func TestListen(t *testing.T) { Requests: data, Imps: data, } - cache.Save(context.Background(), save.Requests, save.Imps) + cache.Requests.Save(context.Background(), save.Requests) + cache.Requests.Save(context.Background(), save.Imps) config = fmt.Sprintf(`{"id": "%s", "updated": true}`, id) data = map[string]json.RawMessage{id: json.RawMessage(config)} @@ -54,7 +53,8 @@ func TestListen(t *testing.T) { ep.saves <- save <-saveOccurred - requestData, impData := cache.Get(context.Background(), idSlice, idSlice) + requestData := cache.Requests.Get(context.Background(), idSlice) + impData := cache.Imps.Get(context.Background(), idSlice) if !reflect.DeepEqual(requestData, data) || !reflect.DeepEqual(impData, data) { t.Error("Update failed") } @@ -67,7 +67,8 @@ func TestListen(t *testing.T) { ep.invalidations <- invalidation <-invalidateOccurred - requestData, impData = cache.Get(context.Background(), idSlice, idSlice) + requestData = cache.Requests.Get(context.Background(), idSlice) + impData = cache.Imps.Get(context.Background(), idSlice) if len(requestData) > 0 || len(impData) > 0 { t.Error("Invalidate failed") } diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go index a31b9989bd0..e9716e08a23 100644 --- a/stored_requests/fetcher.go +++ b/stored_requests/fetcher.go @@ -37,9 +37,9 @@ type CategoryFetcher interface { // AllFetcher is an interface that encapsulates both the original Fetcher and the CategoryFetcher type AllFetcher interface { - FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) - FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) - FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) + Fetcher + AccountFetcher + CategoryFetcher } // NotFoundError is an error type to flag that an ID was not found by the Fetcher. @@ -62,7 +62,11 @@ func (e NotFoundError) Error() string { // Cache is an intermediate layer which can be used to create more complex Fetchers by composition. // Implementations must be safe for concurrent access by multiple goroutines. // To add a Cache layer in front of a Fetcher, see WithCache() -type Cache interface { +type Cache struct { + Requests CacheJSON + Imps CacheJSON +} +type CacheJSON interface { // Get works much like Fetcher.FetchRequests, with a few exceptions: // // 1. Any (actionable) errors should be logged by the implementation, rather than returned. @@ -73,37 +77,33 @@ type Cache interface { // // Nil slices and empty strings are treated as "no ops". That is, a nil requestID will always produce a nil // "stored request data" in the response. - Get(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage) + Get(ctx context.Context, ids []string) (data map[string]json.RawMessage) // Invalidate will ensure that all values associated with the given IDs // are no longer returned by the cache until new values are saved via Update - Invalidate(ctx context.Context, requestIDs []string, impIDs []string) + Invalidate(ctx context.Context, ids []string) // Save will add or overwrite the data in the cache at the given keys - Save(ctx context.Context, requestData map[string]json.RawMessage, impData map[string]json.RawMessage) + Save(ctx context.Context, data map[string]json.RawMessage) } // ComposedCache creates an interface to treat a slice of caches as a single cache -type ComposedCache []Cache +type ComposedCache []CacheJSON // Get will attempt to Get from the caches in the order in which they are in the slice, // stopping as soon as a value is found (or when all caches have been exhausted) -func (c ComposedCache) Get(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage) { - requestData = make(map[string]json.RawMessage, len(requestIDs)) - impData = make(map[string]json.RawMessage, len(impIDs)) +func (c ComposedCache) Get(ctx context.Context, ids []string) (data map[string]json.RawMessage) { + data = make(map[string]json.RawMessage, len(ids)) - remainingReqIDs := requestIDs - remainingImpIDs := impIDs + remainingIDs := ids for _, cache := range c { - cachedReqData, cachedImpData := cache.Get(ctx, remainingReqIDs, remainingImpIDs) - - requestData, remainingReqIDs = updateFromCache(requestData, remainingReqIDs, cachedReqData) - impData, remainingImpIDs = updateFromCache(impData, remainingImpIDs, cachedImpData) + cachedData := cache.Get(ctx, remainingIDs) + data, remainingIDs = updateFromCache(data, remainingIDs, cachedData) - // return if all ids filled - if len(remainingReqIDs) == 0 && len(remainingImpIDs) == 0 { - return + // finish early if all ids filled + if len(remainingIDs) == 0 { + break } } @@ -129,16 +129,16 @@ func updateFromCache(data map[string]json.RawMessage, ids []string, newData map[ } // Invalidate will propagate invalidations to all underlying caches -func (c ComposedCache) Invalidate(ctx context.Context, requestIDs []string, impIDs []string) { +func (c ComposedCache) Invalidate(ctx context.Context, ids []string) { for _, cache := range c { - cache.Invalidate(ctx, requestIDs, impIDs) + cache.Invalidate(ctx, ids) } } // Save will propagate saves to all underlying caches -func (c ComposedCache) Save(ctx context.Context, requestData map[string]json.RawMessage, impData map[string]json.RawMessage) { +func (c ComposedCache) Save(ctx context.Context, data map[string]json.RawMessage) { for _, cache := range c { - cache.Save(ctx, requestData, impData) + cache.Save(ctx, data) } } @@ -148,7 +148,7 @@ type fetcherWithCache struct { metricsEngine pbsmetrics.MetricsEngine } -// WithCache returns a Fetcher which uses the given Cache before delegating to the original. +// WithCache returns a Fetcher which uses the given Caches before delegating to the original. // This can be called multiple times to compose Cache layers onto the backing Fetcher, though // it is usually more desirable to first compose caches with Compose, ensuring propagation of updates // and invalidations through all cache layers. @@ -161,7 +161,9 @@ func WithCache(fetcher AllFetcher, cache Cache, metricsEngine pbsmetrics.Metrics } func (f *fetcherWithCache) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) { - requestData, impData = f.cache.Get(ctx, requestIDs, impIDs) + + requestData = f.cache.Requests.Get(ctx, requestIDs) + impData = f.cache.Imps.Get(ctx, impIDs) // Fixes #311 leftoverImps := findLeftovers(impIDs, impData) @@ -178,7 +180,8 @@ func (f *fetcherWithCache) FetchRequests(ctx context.Context, requestIDs []strin fetcherReqData, fetcherImpData, fetcherErrs := f.fetcher.FetchRequests(ctx, leftoverReqs, leftoverImps) errs = fetcherErrs - f.cache.Save(ctx, fetcherReqData, fetcherImpData) + f.cache.Requests.Save(ctx, fetcherReqData) + f.cache.Imps.Save(ctx, fetcherImpData) requestData = mergeData(requestData, fetcherReqData) impData = mergeData(impData, fetcherImpData) diff --git a/stored_requests/fetcher_test.go b/stored_requests/fetcher_test.go index 1928d1165db..396ba3d04b2 100644 --- a/stored_requests/fetcher_test.go +++ b/stored_requests/fetcher_test.go @@ -12,25 +12,27 @@ import ( "github.com/stretchr/testify/mock" ) -func setupFetcherWithCacheDeps() (*mockCache, *mockFetcher, AllFetcher, *pbsmetrics.MetricsEngineMock) { - cache := &mockCache{} +func setupFetcherWithCacheDeps() (*mockCache, *mockCache, *mockFetcher, AllFetcher, *pbsmetrics.MetricsEngineMock) { + reqCache := &mockCache{} + impCache := &mockCache{} metricsEngine := &pbsmetrics.MetricsEngineMock{} fetcher := &mockFetcher{} - afetcherWithCache := WithCache(fetcher, cache, metricsEngine) + afetcherWithCache := WithCache(fetcher, Cache{reqCache, impCache}, metricsEngine) - return cache, fetcher, afetcherWithCache, metricsEngine + return reqCache, impCache, fetcher, afetcherWithCache, metricsEngine } func TestPerfectCache(t *testing.T) { - cache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() + reqCache, impCache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() impIDs := []string{"known"} reqIDs := []string{"req-id"} ctx := context.Background() - cache.On("Get", ctx, reqIDs, impIDs).Return( + reqCache.On("Get", ctx, reqIDs).Return( map[string]json.RawMessage{ "req-id": json.RawMessage(`{"req":true}`), - }, + }) + impCache.On("Get", ctx, impIDs).Return( map[string]json.RawMessage{ "known": json.RawMessage(`{}`), }) @@ -41,7 +43,8 @@ func TestPerfectCache(t *testing.T) { reqData, impData, errs := aFetcherWithCache.FetchRequests(ctx, reqIDs, impIDs) - cache.AssertExpectations(t) + reqCache.AssertExpectations(t) + impCache.AssertExpectations(t) fetcher.AssertExpectations(t) metricsEngine.AssertExpectations(t) assert.JSONEq(t, `{"req":true}`, string(reqData["req-id"]), "Fetch requests should fetch the right request data") @@ -50,15 +53,16 @@ func TestPerfectCache(t *testing.T) { } func TestImperfectCache(t *testing.T) { - cache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() + reqCache, impCache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() impIDs := []string{"cached", "uncached"} ctx := context.Background() - cache.On("Get", ctx, []string(nil), impIDs).Return( - map[string]json.RawMessage{}, + impCache.On("Get", ctx, impIDs).Return( map[string]json.RawMessage{ "cached": json.RawMessage(`true`), }) + reqCache.On("Get", ctx, []string(nil)).Return( + map[string]json.RawMessage{}) fetcher.On("FetchRequests", ctx, []string{}, []string{"uncached"}).Return( map[string]json.RawMessage{}, @@ -67,11 +71,11 @@ func TestImperfectCache(t *testing.T) { }, []error{}, ) - cache.On("Save", ctx, - map[string]json.RawMessage{}, + impCache.On("Save", ctx, map[string]json.RawMessage{ "uncached": json.RawMessage(`false`), }) + reqCache.On("Save", ctx, map[string]json.RawMessage{}) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheHit, 0) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheMiss, 0) metricsEngine.On("RecordStoredImpCacheResult", pbsmetrics.CacheHit, 1) @@ -79,7 +83,7 @@ func TestImperfectCache(t *testing.T) { reqData, impData, errs := aFetcherWithCache.FetchRequests(ctx, nil, impIDs) - cache.AssertExpectations(t) + impCache.AssertExpectations(t) fetcher.AssertExpectations(t) metricsEngine.AssertExpectations(t) assert.Len(t, reqData, 0, "Fetch requests should return nil if no request IDs were passed") @@ -89,14 +93,15 @@ func TestImperfectCache(t *testing.T) { } func TestMissingData(t *testing.T) { - cache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() + reqCache, impCache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() impIDs := []string{"unknown"} ctx := context.Background() - cache.On("Get", ctx, []string(nil), impIDs).Return( - map[string]json.RawMessage{}, + impCache.On("Get", ctx, impIDs).Return( map[string]json.RawMessage{}, ) + reqCache.On("Get", ctx, []string(nil)).Return( + map[string]json.RawMessage{}) fetcher.On("FetchRequests", ctx, []string{}, impIDs).Return( map[string]json.RawMessage{}, map[string]json.RawMessage{}, @@ -104,8 +109,10 @@ func TestMissingData(t *testing.T) { errors.New("Data not found"), }, ) - cache.On("Save", ctx, + impCache.On("Save", ctx, map[string]json.RawMessage{}, + ) + reqCache.On("Save", ctx, map[string]json.RawMessage{}, ) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheHit, 0) @@ -115,7 +122,8 @@ func TestMissingData(t *testing.T) { reqData, impData, errs := aFetcherWithCache.FetchRequests(ctx, nil, impIDs) - cache.AssertExpectations(t) + reqCache.AssertExpectations(t) + impCache.AssertExpectations(t) fetcher.AssertExpectations(t) metricsEngine.AssertExpectations(t) assert.Len(t, errs, 1, "FetchRequests for missing data should return an error") @@ -125,15 +133,16 @@ func TestMissingData(t *testing.T) { // Prevents #311 func TestCacheSaves(t *testing.T) { - cache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() + reqCache, impCache, fetcher, aFetcherWithCache, metricsEngine := setupFetcherWithCacheDeps() impIDs := []string{"abc", "abc"} ctx := context.Background() - cache.On("Get", ctx, []string(nil), impIDs).Return( - map[string]json.RawMessage{}, + impCache.On("Get", ctx, impIDs).Return( map[string]json.RawMessage{ "abc": json.RawMessage(`{}`), }) + reqCache.On("Get", ctx, []string(nil)).Return( + map[string]json.RawMessage{}) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheHit, 0) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheMiss, 0) metricsEngine.On("RecordStoredImpCacheResult", pbsmetrics.CacheHit, 2) @@ -141,7 +150,7 @@ func TestCacheSaves(t *testing.T) { _, impData, errs := aFetcherWithCache.FetchRequests(ctx, nil, []string{"abc", "abc"}) - cache.AssertExpectations(t) + impCache.AssertExpectations(t) fetcher.AssertExpectations(t) metricsEngine.AssertExpectations(t) assert.Len(t, impData, 1, "FetchRequests should return data only once for duplicate requests") @@ -154,38 +163,34 @@ func TestComposedCache(t *testing.T) { c2 := &mockCache{} c3 := &mockCache{} c4 := &mockCache{} - cache := ComposedCache{c1, c2, c3, c4} + impCache := &mockCache{} + cache := Cache{ + Requests: ComposedCache{c1, c2, c3, c4}, + Imps: impCache, + } metricsEngine := &pbsmetrics.MetricsEngineMock{} fetcher := &mockFetcher{} aFetcherWithCache := WithCache(fetcher, cache, metricsEngine) - impIDs := []string{"1", "2", "3"} reqIDs := []string{"1", "2", "3"} + impIDs := []string{} ctx := context.Background() - c1.On("Get", ctx, reqIDs, impIDs).Return( - map[string]json.RawMessage{ - "1": json.RawMessage(`{"id": "1"}`), - }, + c1.On("Get", ctx, reqIDs).Return( map[string]json.RawMessage{ "1": json.RawMessage(`{"id": "1"}`), }) - c2.On("Get", ctx, []string{"2", "3"}, []string{"2", "3"}).Return( - map[string]json.RawMessage{ - "2": json.RawMessage(`{"id": "2"}`), - }, + c2.On("Get", ctx, []string{"2", "3"}).Return( map[string]json.RawMessage{ "2": json.RawMessage(`{"id": "2"}`), }) - c3.On("Get", ctx, []string{"3"}, []string{"3"}).Return( - map[string]json.RawMessage{ - "3": json.RawMessage(`{"id": "3"}`), - }, + c3.On("Get", ctx, []string{"3"}).Return( map[string]json.RawMessage{ "3": json.RawMessage(`{"id": "3"}`), }) + impCache.On("Get", ctx, []string{}).Return(map[string]json.RawMessage{}) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheHit, 3) metricsEngine.On("RecordStoredReqCacheResult", pbsmetrics.CacheMiss, 0) - metricsEngine.On("RecordStoredImpCacheResult", pbsmetrics.CacheHit, 3) + metricsEngine.On("RecordStoredImpCacheResult", pbsmetrics.CacheHit, 0) metricsEngine.On("RecordStoredImpCacheResult", pbsmetrics.CacheMiss, 0) reqData, impData, errs := aFetcherWithCache.FetchRequests(ctx, reqIDs, impIDs) @@ -193,14 +198,12 @@ func TestComposedCache(t *testing.T) { c1.AssertExpectations(t) c2.AssertExpectations(t) c3.AssertExpectations(t) + impCache.AssertExpectations(t) fetcher.AssertExpectations(t) metricsEngine.AssertExpectations(t) assert.Len(t, reqData, len(reqIDs), "FetchRequests should be able to return all request data from a composed cache") assert.Len(t, impData, len(impIDs), "FetchRequests should be able to return all imp data from a composed cache") assert.Len(t, errs, 0, "FetchRequests shouldn't return an error when trying to use a composed cache") - assert.JSONEq(t, `{"id": "1"}`, string(impData["1"]), "FetchRequests should fetch the right imp data") - assert.JSONEq(t, `{"id": "2"}`, string(impData["2"]), "FetchRequests should fetch the right imp data") - assert.JSONEq(t, `{"id": "3"}`, string(impData["3"]), "FetchRequests should fetch the right imp data") assert.JSONEq(t, `{"id": "1"}`, string(reqData["1"]), "FetchRequests should fetch the right req data") assert.JSONEq(t, `{"id": "2"}`, string(reqData["2"]), "FetchRequests should fetch the right req data") assert.JSONEq(t, `{"id": "3"}`, string(reqData["3"]), "FetchRequests should fetch the right req data") @@ -228,15 +231,15 @@ type mockCache struct { mock.Mock } -func (c *mockCache) Get(ctx context.Context, requestIDs []string, impIDs []string) (map[string]json.RawMessage, map[string]json.RawMessage) { - args := c.Called(ctx, requestIDs, impIDs) - return args.Get(0).(map[string]json.RawMessage), args.Get(1).(map[string]json.RawMessage) +func (c *mockCache) Get(ctx context.Context, ids []string) map[string]json.RawMessage { + args := c.Called(ctx, ids) + return args.Get(0).(map[string]json.RawMessage) } -func (c *mockCache) Save(ctx context.Context, storedRequests map[string]json.RawMessage, storedImps map[string]json.RawMessage) { - c.Called(ctx, storedRequests, storedImps) +func (c *mockCache) Save(ctx context.Context, data map[string]json.RawMessage) { + c.Called(ctx, data) } -func (c *mockCache) Invalidate(ctx context.Context, requestIDs []string, impIDs []string) { - c.Called(ctx, requestIDs, impIDs) +func (c *mockCache) Invalidate(ctx context.Context, ids []string) { + c.Called(ctx, ids) } From 42e676570cf64c244b08b48efdec4de7d4995ec1 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Thu, 10 Sep 2020 13:26:43 -0400 Subject: [PATCH 195/318] Pass Through First Party Context Data (#1479) --- endpoints/openrtb2/auction.go | 16 +- endpoints/openrtb2/auction_test.go | 95 +++++++++- ...valid-context-allowed-with-ext-bidder.json | 32 ++++ ...id-context-allowed-with-prebid-bidder.json | 36 ++++ ...rstpartydata-imp-ext-multiple-bidders.json | 173 +++++++++++++++++ ...ydata-imp-ext-multiple-prebid-bidders.json | 179 ++++++++++++++++++ .../firstpartydata-imp-ext-one-bidder.json | 103 ++++++++++ ...stpartydata-imp-ext-one-prebid-bidder.json | 108 +++++++++++ exchange/utils.go | 38 ++-- openrtb_ext/bidders.go | 3 + openrtb_ext/bidders_test.go | 5 + openrtb_ext/request.go | 4 + 12 files changed, 769 insertions(+), 23 deletions(-) create mode 100644 endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-ext-bidder.json create mode 100644 endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-prebid-bidder.json create mode 100644 exchange/exchangetest/firstpartydata-imp-ext-multiple-bidders.json create mode 100644 exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json create mode 100644 exchange/exchangetest/firstpartydata-imp-ext-one-bidder.json create mode 100644 exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index bc0cd90073f..41c1c1677a5 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -765,8 +765,8 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st } // Also accept bidder exts within imp[...].ext.prebid.bidder - // NOTE: This is not part of the official API, we are not expecting clients - // migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER} + // NOTE: This is not part of the official API yet, so we are not expecting clients + // to migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER} // at this time // https://github.com/prebid/prebid-server/pull/846#issuecomment-476352224 if rawPrebidExt, ok := bidderExts[openrtb_ext.PrebidExtKey]; ok { @@ -785,7 +785,7 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st /* Process all the bidder exts in the request */ disabledBidders := []string{} for bidder, ext := range bidderExts { - if bidder != openrtb_ext.PrebidExtKey { + if isBidderToValidate(bidder) { coreBidder := bidder if tmp, isAlias := aliases[bidder]; isAlias { coreBidder = tmp @@ -820,12 +820,20 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st // TODO #713 Fix this here if len(bidderExts) < 1 { errL = append(errL, fmt.Errorf("request.imp[%d].ext must contain at least one bidder", impIndex)) - return errL } return errL } +func isBidderToValidate(bidder string) bool { + // PrebidExtKey is a special case for the prebid config section and is not considered a bidder. + + // FirstPartyDataContextExtKey is a special case for the first party data context section + // and is not considered a bidder. + + return bidder != openrtb_ext.PrebidExtKey && bidder != openrtb_ext.FirstPartyDataContextExtKey +} + func (deps *endpointDeps) parseBidExt(ext json.RawMessage) (*openrtb_ext.ExtRequest, error) { if len(ext) < 1 { return nil, nil diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 53fea2e0500..7dc244a28c3 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -141,6 +141,16 @@ func TestGoodRequests(t *testing.T) { supplementary.assert(t) } +func TestFirstPartyDataRequests(t *testing.T) { + validRequests := &getResponseFromDirectory{ + dir: "sample-requests/first-party-data", + payloadGetter: getRequestPayload, + messageGetter: nilReturner, + expectedCode: http.StatusOK, + } + validRequests.assert(t) +} + // TestGoodNativeRequests makes sure we return 200s on well-formed Native requests. func TestGoodNativeRequests(t *testing.T) { tests := &getResponseFromDirectory{ @@ -1127,10 +1137,73 @@ func TestDisabledBidder(t *testing.T) { } } -func TestValidateImpExtDisabledBidder(t *testing.T) { - imp := &openrtb.Imp{ - Ext: json.RawMessage(`{"appnexus":{"placement_id":555},"unknownbidder":{"foo":"bar"}}`), +func TestValidateImpExt(t *testing.T) { + testCases := []struct { + description string + impExt json.RawMessage + expectedImpExt string + expectedErrs []error + }{ + { + description: "Empty", + impExt: nil, + expectedImpExt: "", + expectedErrs: []error{errors.New("request.imp[0].ext is required")}, + }, + { + description: "Valid Bidder", + impExt: json.RawMessage(`{"appnexus":{"placement_id":555}}`), + expectedImpExt: `{"appnexus":{"placement_id":555}}`, + expectedErrs: []error{}, + }, + { + description: "Valid Bidder + Disabled Bidder", + impExt: json.RawMessage(`{"appnexus":{"placement_id":555},"unknownbidder":{"foo":"bar"}}`), + expectedImpExt: `{"appnexus":{"placement_id":555}}`, + expectedErrs: []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, + }, + { + description: "Valid Bidder + Disabled Bidder + First Party Data Context", + impExt: json.RawMessage(`{"appnexus":{"placement_id":555},"unknownbidder":{"foo":"bar"},"context":{"data":{"keywords":"prebid server example"}}}`), + expectedImpExt: `{"appnexus":{"placement_id":555},"context":{"data":{"keywords":"prebid server example"}}}`, + expectedErrs: []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, + }, + { + description: "Valid Bidder + First Party Data Context", + impExt: json.RawMessage(`{"appnexus":{"placement_id":555},"context":{"data":{"keywords":"prebid server example"}}}`), + expectedImpExt: `{"appnexus":{"placement_id":555},"context":{"data":{"keywords":"prebid server example"}}}`, + expectedErrs: []error{}, + }, + { + description: "Valid Prebid Ext Bidder", + impExt: json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placement_id":555}}}}`), + expectedImpExt: `{"prebid":{"bidder":{"appnexus":{"placement_id":555}}}}`, + expectedErrs: []error{}, + // request.imp[x].ext.prebid.bidder.{biddername} is only promoted/copied to request.ext.{biddername} if there is at least one disabled bidder. + }, + { + description: "Valid Prebid Ext Bidder + First Party Data Context", + impExt: json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placement_id":555}}} ,"context":{"data":{"keywords":"prebid server example"}}}`), + expectedImpExt: `{"prebid":{"bidder":{"appnexus":{"placement_id":555}}},"context":{"data":{"keywords":"prebid server example"}}}`, + expectedErrs: []error{}, + // request.imp[x].ext.prebid.bidder.{biddername} is only promoted/copied to request.ext.{biddername} if there is at least one disabled bidder. + }, + { + description: "Valid Prebid Ext Bidder + Disabled Bidder", + impExt: json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placement_id":555},"unknownbidder":{"foo":"bar"}}}}`), + expectedImpExt: `{"prebid":{"bidder":{"appnexus":{"placement_id": 555},"unknownbidder":{"foo":"bar"}}},"appnexus":{"placement_id":555}}`, + expectedErrs: []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, + // request.imp[x].ext.prebid.bidder.{biddername} disabled bidders are not removed. if there is a disabled bidder, the valid ones are promoted/copied to request.ext.{biddername}. + }, + { + description: "Valid Prebid Ext Bidder + Disabled Bidder + First Party Data Context", + impExt: json.RawMessage(`{"prebid":{"bidder":{"appnexus":{"placement_id":555},"unknownbidder":{"foo":"bar"}}},"context":{"data":{"keywords":"prebid server example"}}}`), + expectedImpExt: `{"prebid":{"bidder":{"appnexus":{"placement_id": 555},"unknownbidder":{"foo":"bar"}}},"appnexus":{"placement_id":555},"context":{"data":{"keywords":"prebid server example"}}}`, + expectedErrs: []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, + // request.imp[x].ext.prebid.bidder.{biddername} disabled bidders are not removed. if there is a disabled bidder, the valid ones are promoted/copied to request.ext.{biddername}. + }, } + deps := &endpointDeps{ &nobidExchange{}, newParamsValidator(t), @@ -1149,9 +1222,19 @@ func TestValidateImpExtDisabledBidder(t *testing.T) { nil, hardcodedResponseIPValidator{response: true}, } - errs := deps.validateImpExt(imp, nil, 0) - assert.JSONEq(t, `{"appnexus":{"placement_id":555}}`, string(imp.Ext)) - assert.Equal(t, []error{&errortypes.BidderTemporarilyDisabled{Message: "The bidder 'unknownbidder' has been disabled."}}, errs) + + for _, test := range testCases { + imp := &openrtb.Imp{Ext: test.impExt} + + errs := deps.validateImpExt(imp, nil, 0) + + if len(test.expectedImpExt) > 0 { + assert.JSONEq(t, test.expectedImpExt, string(imp.Ext)) + } else { + assert.Empty(t, imp.Ext) + } + assert.Equal(t, test.expectedErrs, errs) + } } func validRequest(t *testing.T, filename string) string { diff --git a/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-ext-bidder.json b/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-ext-bidder.json new file mode 100644 index 00000000000..aa205fc55ce --- /dev/null +++ b/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-ext-bidder.json @@ -0,0 +1,32 @@ +{ + "description": "The imp.ext.context field is valid for First Party Data and should be exempted from bidder name validation.", + + "requestPayload": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-prebid-bidder.json b/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-prebid-bidder.json new file mode 100644 index 00000000000..1616e84b416 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/first-party-data/valid-context-allowed-with-prebid-bidder.json @@ -0,0 +1,36 @@ +{ + "description": "The imp.ext.context field is valid for First Party Data and should be exempted from bidder name validation.", + + "requestPayload": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 12883451 + } + } + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } +} \ No newline at end of file diff --git a/exchange/exchangetest/firstpartydata-imp-ext-multiple-bidders.json b/exchange/exchangetest/firstpartydata-imp-ext-multiple-bidders.json new file mode 100644 index 00000000000..8004c3c2646 --- /dev/null +++ b/exchange/exchangetest/firstpartydata-imp-ext-multiple-bidders.json @@ -0,0 +1,173 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "appnexus": { + "placementId": 1 + }, + "rubicon": { + "accountId": 1, + "siteId": 2, + "zoneId": 3 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "placementId": 1 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1" + }, + "bidType": "banner" + }] + } + } + }, + "rubicon": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "accountId": 1, + "siteId": 2, + "zoneId": 3 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "rubi-bid", + "impid": "some-imp-id", + "price": 0.4, + "w": 200, + "h": 500, + "crid": "creative-2" + }, + "bidType": "banner" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }, { + "seat": "rubicon", + "bid": [{ + "id": "rubi-bid", + "impid": "some-imp-id", + "price": 0.4, + "w": 200, + "h": 500, + "crid": "creative-2", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json new file mode 100644 index 00000000000..d62afccf426 --- /dev/null +++ b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json @@ -0,0 +1,179 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + }, + "rubicon": { + "accountId": 1, + "siteId": 2, + "zoneId": 3 + } + } + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "placementId": 1 + }, + "prebid": {}, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1" + }, + "bidType": "banner" + }] + } + } + }, + "rubicon": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "accountId": 1, + "siteId": 2, + "zoneId": 3 + }, + "prebid": {}, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "rubi-bid", + "impid": "some-imp-id", + "price": 0.4, + "w": 200, + "h": 500, + "crid": "creative-2" + }, + "bidType": "banner" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }, { + "seat": "rubicon", + "bid": [{ + "id": "rubi-bid", + "impid": "some-imp-id", + "price": 0.4, + "w": 200, + "h": 500, + "crid": "creative-2", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/firstpartydata-imp-ext-one-bidder.json b/exchange/exchangetest/firstpartydata-imp-ext-one-bidder.json new file mode 100644 index 00000000000..6f0bab9529c --- /dev/null +++ b/exchange/exchangetest/firstpartydata-imp-ext-one-bidder.json @@ -0,0 +1,103 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "appnexus": { + "placementId": 1 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "placementId": 1 + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1" + }, + "bidType": "banner" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json new file mode 100644 index 00000000000..1610b9ea47e --- /dev/null +++ b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json @@ -0,0 +1,108 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + }, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "some-imp-id", + "banner": { + "format": [{ + "w": 600, + "h": 500 + }, { + "w": 300, + "h": 600 + }] + }, + "ext": { + "bidder": { + "placementId": 1 + }, + "prebid": {}, + "context": { + "data": { + "keywords": "prebid server example" + } + } + } + }] + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "pbsSeatBid": { + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1" + }, + "bidType": "banner" + }] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "some-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-1", + "ext": { + "prebid": { + "type": "banner" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/utils.go b/exchange/utils.go index 2e9e4dc8f80..5863f6c8530 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -273,13 +273,18 @@ func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, []error) { imp := imps[i] impExt := impExts[i] + var firstPartyDataContext json.RawMessage + if context, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists { + firstPartyDataContext = context + } + rawPrebidExt, ok := impExt[openrtb_ext.PrebidExtKey] if ok { var prebidExt openrtb_ext.ExtImpPrebid if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil { - if errs := sanitizedImpCopy(&imp, prebidExt.Bidder, rawPrebidExt, &splitImps); errs != nil { + if errs := sanitizedImpCopy(&imp, prebidExt.Bidder, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil { errList = append(errList, errs...) } @@ -287,7 +292,7 @@ func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, []error) { } } - if errs := sanitizedImpCopy(&imp, impExt, rawPrebidExt, &splitImps); errs != nil { + if errs := sanitizedImpCopy(&imp, impExt, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil { errList = append(errList, errs...) } } @@ -295,35 +300,38 @@ func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, []error) { return splitImps, nil } -// sanitizedImpCopy returns a copy of imp with its ext filtered so that only "prebid" and bidder params exist. +// sanitizedImpCopy returns a copy of imp with its ext filtered so that only "prebid", "context", and bidder params exist. // It will not mutate the input imp. // This function will write the new imps to the output map passed in func sanitizedImpCopy(imp *openrtb.Imp, bidderExts map[string]json.RawMessage, rawPrebidExt json.RawMessage, + firstPartyDataContext json.RawMessage, out *map[string][]openrtb.Imp) []error { var prebidExt map[string]json.RawMessage var errs []error - // We don't want to include other demand partners' bidder params - // in the sanitized imp if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil { - delete(prebidExt, "bidder") - - var err error - if rawPrebidExt, err = json.Marshal(prebidExt); err != nil { - errs = append(errs, err) + // Remove the entire bidder field. We will already have the content we need in bidderExts. We + // don't want to include other demand partners' bidder params in the sanitized imp. + if _, hasBidderField := prebidExt["bidder"]; hasBidderField { + delete(prebidExt, "bidder") + + var err error + if rawPrebidExt, err = json.Marshal(prebidExt); err != nil { + errs = append(errs, err) + } } } for bidder, ext := range bidderExts { - if bidder == openrtb_ext.PrebidExtKey { + if bidder == openrtb_ext.PrebidExtKey || bidder == openrtb_ext.FirstPartyDataContextExtKey { continue } impCopy := *imp - newExt := make(map[string]json.RawMessage, 2) + newExt := make(map[string]json.RawMessage, 3) newExt["bidder"] = ext @@ -331,6 +339,10 @@ func sanitizedImpCopy(imp *openrtb.Imp, newExt[openrtb_ext.PrebidExtKey] = rawPrebidExt } + if len(firstPartyDataContext) > 0 { + newExt[openrtb_ext.FirstPartyDataContextExtKey] = firstPartyDataContext + } + rawExt, err := json.Marshal(newExt) if err != nil { errs = append(errs, err) @@ -392,7 +404,7 @@ func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderN } // parseImpExts does a partial-unmarshal of the imp[].Ext field. -// The keys in the returned map are expected to be "prebid", core BidderNames, or Aliases for this request. +// The keys in the returned map are expected to be "prebid", "context", core BidderNames, or Aliases for this request. func parseImpExts(imps []openrtb.Imp) ([]map[string]json.RawMessage, error) { exts := make([]map[string]json.RawMessage, len(imps)) // Loop over every impression in the request diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 761f53d441e..876eeab86bd 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -20,6 +20,9 @@ type BidderName string // BidderNameGeneral is reserved for non-bidder specific messages when using a map keyed on the bidder name. const BidderNameGeneral = BidderName("general") +// BidderNameContext is reserved for first party data. +const BidderNameContext = BidderName("context") + // These names _must_ coincide with the bidder code in Prebid.js, if an adapter also exists in that project. // Please keep these (and the BidderMap) alphabetized to minimize merge conflicts among adapter submissions. // The bidder name 'general' is not allowed since it has special meaning in message maps. diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go index d49b23237ed..9f05f526905 100644 --- a/openrtb_ext/bidders_test.go +++ b/openrtb_ext/bidders_test.go @@ -61,3 +61,8 @@ func TestBidderListDoesNotDefineGeneral(t *testing.T) { bidders := BidderList() assert.NotContains(t, bidders, BidderNameGeneral) } + +func TestBidderListDoesNotDefineContext(t *testing.T) { + bidders := BidderList() + assert.NotContains(t, bidders, BidderNameContext) +} diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index d6edf47f939..42ac9d9d4b9 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -5,6 +5,10 @@ import ( "errors" ) +// FirstPartyDataContextExtKey defines the field name within bidrequest.ext reserved +// for first party data support. +const FirstPartyDataContextExtKey string = "context" + // ExtRequest defines the contract for bidrequest.ext type ExtRequest struct { Prebid ExtRequestPrebid `json:"prebid"` From fa23f5c226df99a9a4ef318100fdb7d84d3e40fa Mon Sep 17 00:00:00 2001 From: hdeodhar <35999856+hdeodhar@users.noreply.github.com> Date: Thu, 10 Sep 2020 22:33:14 +0100 Subject: [PATCH 196/318] Added new size 640x360 (Id: 198) (#1490) --- adapters/rubicon/rubicon.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 56ae7b2f792..7d6e0e12039 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -236,6 +236,7 @@ var rubiSizeMap = map[rubiSize]int{ {w: 800, h: 250}: 125, {w: 200, h: 600}: 126, {w: 640, h: 320}: 156, + {w: 640, h: 360}: 198, } // defines the contract for bidrequest.user.ext.eids[i].ext From 65c6c3608d0cca20168a69c8cf14d043a0d39a45 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Mon, 14 Sep 2020 07:19:40 -0700 Subject: [PATCH 197/318] Refactor: move getAccount to accounts package (from openrtb2) (#1483) --- account/account.go | 69 +++++++++++++++++++++ account/account_test.go | 94 +++++++++++++++++++++++++++++ endpoints/openrtb2/amp_auction.go | 3 +- endpoints/openrtb2/auction.go | 56 +---------------- endpoints/openrtb2/auction_test.go | 70 +-------------------- endpoints/openrtb2/video_auction.go | 3 +- 6 files changed, 170 insertions(+), 125 deletions(-) create mode 100644 account/account.go create mode 100644 account/account_test.go diff --git a/account/account.go b/account/account.go new file mode 100644 index 00000000000..2f27b61efab --- /dev/null +++ b/account/account.go @@ -0,0 +1,69 @@ +package account + +import ( + "context" + "encoding/json" + "fmt" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/pbsmetrics" + "github.com/prebid/prebid-server/stored_requests" +) + +// GetAccount looks up the config.Account object referenced by the given accountID, with access rules applied +func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_requests.AccountFetcher, accountID string) (account *config.Account, errs []error) { + // Check BlacklistedAcctMap until we have deprecated it + if _, found := cfg.BlacklistedAcctMap[accountID]; found { + return nil, []error{&errortypes.BlacklistedAcct{ + Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", accountID), + }} + } + if cfg.AccountRequired && accountID == pbsmetrics.PublisherUnknown { + return nil, []error{&errortypes.AcctRequired{ + Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), + }} + } + if accountJSON, accErrs := fetcher.FetchAccount(ctx, accountID); len(accErrs) > 0 || accountJSON == nil { + // accountID does not reference a valid account + for _, e := range accErrs { + if _, ok := e.(stored_requests.NotFoundError); !ok { + errs = append(errs, e) + } + } + if cfg.AccountRequired && cfg.AccountDefaults.Disabled { + errs = append(errs, &errortypes.AcctRequired{ + Message: fmt.Sprintf("Prebid-server could not verify the Account ID. Please reach out to the prebid server host."), + }) + return nil, errs + } + // Make a copy of AccountDefaults instead of taking a reference, + // to preserve original accountID in case is needed to check NonStandardPublisherMap + pubAccount := cfg.AccountDefaults + pubAccount.ID = accountID + account = &pubAccount + } else { + // accountID resolved to a valid account, merge with AccountDefaults for a complete config + account = &config.Account{} + completeJSON, err := jsonpatch.MergePatch(cfg.AccountDefaultsJSON(), accountJSON) + if err == nil { + err = json.Unmarshal(completeJSON, account) + } + if err != nil { + errs = append(errs, err) + return nil, errs + } + // Fill in ID if needed, so it can be left out of account definition + if len(account.ID) == 0 { + account.ID = accountID + } + } + if account.Disabled { + errs = append(errs, &errortypes.BlacklistedAcct{ + Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", accountID), + }) + return nil, errs + } + return account, nil +} diff --git a/account/account_test.go b/account/account_test.go new file mode 100644 index 00000000000..0d192f18510 --- /dev/null +++ b/account/account_test.go @@ -0,0 +1,94 @@ +package account + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/pbsmetrics" + "github.com/prebid/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" +) + +var mockAccountData = map[string]json.RawMessage{ + "valid_acct": json.RawMessage(`{"disabled":false}`), + "disabled_acct": json.RawMessage(`{"disabled":true}`), +} + +type mockAccountFetcher struct { +} + +func (af mockAccountFetcher) FetchAccount(ctx context.Context, accountID string) (json.RawMessage, []error) { + if account, ok := mockAccountData[accountID]; ok { + return account, nil + } + return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} +} + +func TestGetAccount(t *testing.T) { + unknown := pbsmetrics.PublisherUnknown + testCases := []struct { + accountID string + // account_required + required bool + // account_defaults.disabled + disabled bool + // expected error, or nil if account should be found + err error + }{ + // Blacklisted account is always rejected even in permissive setup + {accountID: "bad_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, + + // empty pubID + {accountID: unknown, required: false, disabled: false, err: nil}, + {accountID: unknown, required: true, disabled: false, err: &errortypes.AcctRequired{}}, + {accountID: unknown, required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, + + // pubID given but is not a valid host account (does not exist) + {accountID: "doesnt_exist_acct", required: false, disabled: false, err: nil}, + {accountID: "doesnt_exist_acct", required: true, disabled: false, err: nil}, + {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "doesnt_exist_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, + + // pubID given and matches a valid host account with Disabled: false + {accountID: "valid_acct", required: false, disabled: false, err: nil}, + {accountID: "valid_acct", required: true, disabled: false, err: nil}, + {accountID: "valid_acct", required: false, disabled: true, err: nil}, + {accountID: "valid_acct", required: true, disabled: true, err: nil}, + + // pubID given and matches a host account explicitly disabled (Disabled: true on account json) + {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}}, + } + + for _, test := range testCases { + description := fmt.Sprintf(`ID=%s/required=%t/disabled=%t`, test.accountID, test.required, test.disabled) + t.Run(description, func(t *testing.T) { + cfg := &config.Configuration{ + BlacklistedAcctMap: map[string]bool{"bad_acct": true}, + AccountRequired: test.required, + AccountDefaults: config.Account{Disabled: test.disabled}, + } + fetcher := &mockAccountFetcher{} + assert.NoError(t, cfg.MarshalAccountDefaults()) + + account, errors := GetAccount(context.Background(), cfg, fetcher, test.accountID) + + if test.err == nil { + assert.Empty(t, errors) + assert.Equal(t, test.accountID, account.ID, "account.ID must match requested ID") + assert.Equal(t, false, account.Disabled, "returned account must not be disabled") + } else { + assert.NotEmpty(t, errors, "expected errors but got success") + assert.Nil(t, account, "return account must be nil on error") + assert.IsType(t, test.err, errors[0], "error is of unexpected type") + } + }) + } +} diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 54f4706902d..1e92569e260 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -16,6 +16,7 @@ import ( "github.com/golang/glog" "github.com/julienschmidt/httprouter" "github.com/mxmCherry/openrtb" + accountService "github.com/prebid/prebid-server/account" "github.com/prebid/prebid-server/analytics" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/errortypes" @@ -158,7 +159,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h } labels.PubID = getAccountID(req.Site.Publisher) // Look up account now that we have resolved the pubID value - account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + account, acctIDErrs := accountService.GetAccount(ctx, deps.cfg, deps.accounts, labels.PubID) if len(acctIDErrs) > 0 { errL = append(errL, acctIDErrs...) httpStatus := http.StatusBadRequest diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 41c1c1677a5..d6cbc2285fb 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -22,6 +22,7 @@ import ( "github.com/mxmCherry/openrtb" "github.com/mxmCherry/openrtb/native" nativeRequests "github.com/mxmCherry/openrtb/native/request" + accountService "github.com/prebid/prebid-server/account" "github.com/prebid/prebid-server/analytics" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/errortypes" @@ -156,7 +157,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http } // Look up account now that we have resolved the pubID value - account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + account, acctIDErrs := accountService.GetAccount(ctx, deps.cfg, deps.accounts, labels.PubID) if len(acctIDErrs) > 0 { errL = append(errL, acctIDErrs...) writeError(errL, w, &labels) @@ -1317,56 +1318,3 @@ func getAccountID(pub *openrtb.Publisher) string { } return pbsmetrics.PublisherUnknown } - -func (deps *endpointDeps) getAccount(ctx context.Context, pubID string) (account *config.Account, errs []error) { - // Check BlacklistedAcctMap until we have deprecated it - if _, found := deps.cfg.BlacklistedAcctMap[pubID]; found { - return nil, []error{&errortypes.BlacklistedAcct{ - Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", pubID), - }} - } - if deps.cfg.AccountRequired && pubID == pbsmetrics.PublisherUnknown { - return nil, []error{&errortypes.AcctRequired{ - Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), - }} - } - if accountJSON, accErrs := deps.accounts.FetchAccount(ctx, pubID); len(accErrs) > 0 || accountJSON == nil { - // pubID does not reference a valid account - if len(accErrs) > 0 { - errs = append(errs, errs...) - } - if deps.cfg.AccountRequired && deps.cfg.AccountDefaults.Disabled { - errs = append(errs, &errortypes.AcctRequired{ - Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), - }) - return nil, errs - } - // Make a copy of AccountDefaults instead of taking a reference, - // to preserve original pubID in case is needed to check NonStandardPublisherMap - pubAccount := deps.cfg.AccountDefaults - pubAccount.ID = pubID - account = &pubAccount - } else { - // pubID resolved to a valid account, merge with AccountDefaults for a complete config - account = &config.Account{} - completeJSON, err := jsonpatch.MergePatch(deps.cfg.AccountDefaultsJSON(), accountJSON) - if err == nil { - err = json.Unmarshal(completeJSON, account) - } - if err != nil { - errs = append(errs, err) - return nil, errs - } - // Fill in ID if needed, so it can be left out of account definition - if len(account.ID) == 0 { - account.ID = pubID - } - } - if account.Disabled { - errs = append(errs, &errortypes.BlacklistedAcct{ - Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", pubID), - }) - return nil, errs - } - return -} diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 7dc244a28c3..925cffcebeb 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1984,8 +1984,7 @@ func (cf mockStoredReqFetcher) FetchRequests(ctx context.Context, requestIDs []s } var mockAccountData = map[string]json.RawMessage{ - "valid_acct": json.RawMessage(`{"disabled":false}`), - "disabled_acct": json.RawMessage(`{"disabled":true}`), + "valid_acct": json.RawMessage(`{"disabled":false}`), } type mockAccountFetcher struct { @@ -2058,70 +2057,3 @@ type hardcodedResponseIPValidator struct { func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool { return v.response } - -func TestGetAccount(t *testing.T) { - unknown := pbsmetrics.PublisherUnknown - testCases := []struct { - accountID string - // account_required - required bool - // account_defaults.disabled - disabled bool - // expected error, or nil if account should be found - err error - }{ - // Blacklisted account is always rejected even in permissive setup - {accountID: "bad_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, - - // empty pubID - {accountID: unknown, required: false, disabled: false, err: nil}, - {accountID: unknown, required: true, disabled: false, err: &errortypes.AcctRequired{}}, - {accountID: unknown, required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, - {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, - - // pubID given but is not a valid host account (does not exist) - {accountID: "doesnt_exist_acct", required: false, disabled: false, err: nil}, - {accountID: "doesnt_exist_acct", required: true, disabled: false, err: nil}, - {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, - {accountID: "doesnt_exist_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, - - // pubID given and matches a valid host account with Disabled: false - {accountID: "valid_acct", required: false, disabled: false, err: nil}, - {accountID: "valid_acct", required: true, disabled: false, err: nil}, - {accountID: "valid_acct", required: false, disabled: true, err: nil}, - {accountID: "valid_acct", required: true, disabled: true, err: nil}, - - // pubID given and matches a host account explicitly disabled (Disabled: true on account json) - {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}}, - } - - for _, test := range testCases { - description := fmt.Sprintf(`ID=%s/required=%t/disabled=%t`, test.accountID, test.required, test.disabled) - t.Run(description, func(t *testing.T) { - deps := &endpointDeps{ - cfg: &config.Configuration{ - BlacklistedAcctMap: map[string]bool{"bad_acct": true}, - AccountRequired: test.required, - AccountDefaults: config.Account{Disabled: test.disabled}, - }, - accounts: &mockAccountFetcher{}, - } - assert.NoError(t, deps.cfg.MarshalAccountDefaults()) - - account, errors := deps.getAccount(context.Background(), test.accountID) - - if test.err == nil { - assert.Empty(t, errors) - assert.Equal(t, test.accountID, account.ID, "account.ID must match requested ID") - assert.Equal(t, false, account.Disabled, "returned account must not be disabled") - } else { - assert.NotEmpty(t, errors, "expected errors but got success") - assert.Nil(t, account, "return account must be nil on error") - assert.IsType(t, test.err, errors[0], "error is of unexpected type") - } - }) - } -} diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index f5494751cc2..ab5634c7853 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -23,6 +23,7 @@ import ( "github.com/golang/glog" "github.com/julienschmidt/httprouter" "github.com/mxmCherry/openrtb" + accountService "github.com/prebid/prebid-server/account" "github.com/prebid/prebid-server/analytics" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/exchange" @@ -255,7 +256,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } // Look up account now that we have resolved the pubID value - account, acctIDErrs := deps.getAccount(ctx, labels.PubID) + account, acctIDErrs := accountService.GetAccount(ctx, deps.cfg, deps.accounts, labels.PubID) if len(acctIDErrs) > 0 { handleError(&labels, w, acctIDErrs, &vo, &debugLog) return From e7d0babd32833e6fba144d020b4a09c0aacdeb50 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Tue, 15 Sep 2020 10:21:18 -0400 Subject: [PATCH 198/318] Fixed TCF2 Geo Only Enforcement (#1492) --- exchange/utils.go | 4 +- privacy/enforcement.go | 30 +++-- privacy/enforcement_test.go | 131 ++++++++++------------ privacy/scrubber.go | 52 +++++++-- privacy/scrubber_test.go | 218 +++++++++++++++--------------------- 5 files changed, 213 insertions(+), 222 deletions(-) diff --git a/exchange/utils.go b/exchange/utils.go index 5863f6c8530..22b28adcacb 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -107,12 +107,10 @@ func cleanOpenRTBRequests(ctx context.Context, coreBidder := resolveBidder(bidder.String(), aliases) var publisherID = labels.PubID - ok, geo, id, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) - privacyEnforcement.GDPR = !ok && err == nil + _, geo, id, err := gDPR.PersonalInfoAllowed(ctx, coreBidder, publisherID, consent) privacyEnforcement.GDPRGeo = !geo && err == nil privacyEnforcement.GDPRID = !id && err == nil } else { - privacyEnforcement.GDPR = false privacyEnforcement.GDPRGeo = false privacyEnforcement.GDPRID = false } diff --git a/privacy/enforcement.go b/privacy/enforcement.go index 9c23c320680..3f157329cf6 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -8,7 +8,6 @@ import ( type Enforcement struct { CCPA bool COPPA bool - GDPR bool GDPRGeo bool GDPRID bool LMT bool @@ -16,7 +15,7 @@ type Enforcement struct { // Any returns true if at least one privacy policy requires enforcement. func (e Enforcement) Any() bool { - return e.CCPA || e.COPPA || e.GDPR || e.GDPRGeo || e.GDPRID || e.LMT + return e.CCPA || e.COPPA || e.GDPRGeo || e.GDPRID || e.LMT } // Apply cleans personally identifiable information from an OpenRTB bid request. @@ -26,17 +25,33 @@ func (e Enforcement) Apply(bidRequest *openrtb.BidRequest, ampGDPRException bool func (e Enforcement) apply(bidRequest *openrtb.BidRequest, ampGDPRException bool, scrubber Scrubber) { if bidRequest != nil && e.Any() { - bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) + bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceIDScrubStrategy(), e.getIPv4ScrubStrategy(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(ampGDPRException), e.getGeoScrubStrategy()) } } +func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID { + if e.COPPA || e.GDPRID || e.CCPA || e.LMT { + return ScrubStrategyDeviceIDAll + } + + return ScrubStrategyDeviceIDNone +} + +func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 { + if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT { + return ScrubStrategyIPV4Lowest8 + } + + return ScrubStrategyIPV4None +} + func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 { if e.COPPA { return ScrubStrategyIPV6Lowest32 } - if e.GDPR || e.CCPA || e.LMT { + if e.GDPRGeo || e.CCPA || e.LMT { return ScrubStrategyIPV6Lowest16 } @@ -60,12 +75,11 @@ func (e Enforcement) getUserScrubStrategy(ampGDPRException bool) ScrubStrategyUs return ScrubStrategyUserIDAndDemographic } - if e.GDPR && ampGDPRException { - return ScrubStrategyUserNone + if e.CCPA || e.LMT { + return ScrubStrategyUserID } - // If no user scrubbing is needed, then return none, else scrub ID (COPPA checked above) - if e.CCPA || e.GDPRID || e.LMT { + if e.GDPRID && !ampGDPRException { return ScrubStrategyUserID } diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index ef02e28147a..0cf36a614c4 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -19,7 +19,6 @@ func TestAny(t *testing.T) { enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: false, @@ -31,7 +30,6 @@ func TestAny(t *testing.T) { enforcement: Enforcement{ CCPA: true, COPPA: true, - GDPR: true, GDPRGeo: true, GDPRID: true, LMT: true, @@ -43,7 +41,6 @@ func TestAny(t *testing.T) { enforcement: Enforcement{ CCPA: false, COPPA: true, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: true, @@ -63,6 +60,8 @@ func TestApply(t *testing.T) { description string enforcement Enforcement ampGDPRException bool + expectedDeviceID ScrubStrategyDeviceID + expectedDeviceIPv4 ScrubStrategyIPV4 expectedDeviceIPv6 ScrubStrategyIPV6 expectedDeviceGeo ScrubStrategyGeo expectedUser ScrubStrategyUser @@ -73,12 +72,12 @@ func TestApply(t *testing.T) { enforcement: Enforcement{ CCPA: true, COPPA: true, - GDPR: true, GDPRGeo: true, GDPRID: true, LMT: true, }, - ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, expectedUser: ScrubStrategyUserIDAndDemographic, @@ -89,12 +88,12 @@ func TestApply(t *testing.T) { enforcement: Enforcement{ CCPA: true, COPPA: false, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: false, }, - ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, expectedUser: ScrubStrategyUserID, @@ -105,124 +104,97 @@ func TestApply(t *testing.T) { enforcement: Enforcement{ CCPA: false, COPPA: true, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: false, }, - ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, expectedDeviceGeo: ScrubStrategyGeoFull, expectedUser: ScrubStrategyUserIDAndDemographic, expectedUserGeo: ScrubStrategyGeoFull, }, { - description: "GDPR Only", + description: "GDPR Only - Full", enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: true, GDPRGeo: true, GDPRID: true, LMT: false, }, ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, expectedUser: ScrubStrategyUserID, expectedUserGeo: ScrubStrategyGeoReducedPrecision, }, { - description: "GDPR Only, ampGDPRException", + description: "GDPR Only - Full - AMP Exception", enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: true, GDPRGeo: true, GDPRID: true, LMT: false, }, ampGDPRException: true, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, expectedUser: ScrubStrategyUserNone, expectedUserGeo: ScrubStrategyGeoReducedPrecision, }, { - description: "CCPA Only, ampGDPRException", - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPR: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - ampGDPRException: true, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserID, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "COPPA and GDPR, ampGDPRException", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPR: true, - GDPRGeo: true, - GDPRID: true, - LMT: false, - }, - ampGDPRException: true, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - { - description: "GDPR Only, no Geo", + description: "GDPR Only - ID Only", enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: true, GDPRGeo: false, GDPRID: true, LMT: false, }, ampGDPRException: false, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4None, + expectedDeviceIPv6: ScrubStrategyIPV6None, expectedDeviceGeo: ScrubStrategyGeoNone, expectedUser: ScrubStrategyUserID, expectedUserGeo: ScrubStrategyGeoNone, }, { - description: "GDPR Only, Geo only", + description: "GDPR Only - ID Only - AMP Exception", enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: false, - GDPRGeo: true, - GDPRID: false, + GDPRGeo: false, + GDPRID: true, LMT: false, }, - ampGDPRException: false, + ampGDPRException: true, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4None, expectedDeviceIPv6: ScrubStrategyIPV6None, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, + expectedDeviceGeo: ScrubStrategyGeoNone, expectedUser: ScrubStrategyUserNone, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, + expectedUserGeo: ScrubStrategyGeoNone, }, { - description: "GDPR Only, ID exception", + description: "GDPR Only - Geo Only", enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: true, GDPRGeo: true, GDPRID: false, LMT: false, }, ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDNone, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, expectedUser: ScrubStrategyUserNone, @@ -233,32 +205,50 @@ func TestApply(t *testing.T) { enforcement: Enforcement{ CCPA: false, COPPA: false, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: true, }, - ampGDPRException: false, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, expectedUser: ScrubStrategyUserID, expectedUserGeo: ScrubStrategyGeoReducedPrecision, }, { - description: "LMT Only, ampGDPRException", + description: "Interactions: COPPA Only + AMP Exception", enforcement: Enforcement{ CCPA: false, - COPPA: false, - GDPR: false, + COPPA: true, GDPRGeo: false, GDPRID: false, - LMT: true, + LMT: false, }, ampGDPRException: true, - expectedDeviceIPv6: ScrubStrategyIPV6Lowest16, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserID, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, + expectedDeviceGeo: ScrubStrategyGeoFull, + expectedUser: ScrubStrategyUserIDAndDemographic, + expectedUserGeo: ScrubStrategyGeoFull, + }, + { + description: "Interactions: COPPA + GDPR Full + AMP Exception", + enforcement: Enforcement{ + CCPA: false, + COPPA: true, + GDPRGeo: true, + GDPRID: true, + LMT: false, + }, + ampGDPRException: true, + expectedDeviceID: ScrubStrategyDeviceIDAll, + expectedDeviceIPv4: ScrubStrategyIPV4Lowest8, + expectedDeviceIPv6: ScrubStrategyIPV6Lowest32, + expectedDeviceGeo: ScrubStrategyGeoFull, + expectedUser: ScrubStrategyUserIDAndDemographic, + expectedUserGeo: ScrubStrategyGeoFull, }, } @@ -271,7 +261,7 @@ func TestApply(t *testing.T) { replacedUser := &openrtb.User{} m := &mockScrubber{} - m.On("ScrubDevice", req.Device, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once() + m.On("ScrubDevice", req.Device, test.expectedDeviceID, test.expectedDeviceIPv4, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once() m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(replacedUser).Once() test.enforcement.apply(req, test.ampGDPRException, m) @@ -290,7 +280,6 @@ func TestApplyNoneApplicable(t *testing.T) { enforcement := Enforcement{ CCPA: false, COPPA: false, - GDPR: false, GDPRGeo: false, GDPRID: false, LMT: false, @@ -315,8 +304,8 @@ type mockScrubber struct { mock.Mock } -func (m *mockScrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { - args := m.Called(device, ipv6, geo) +func (m *mockScrubber) ScrubDevice(device *openrtb.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { + args := m.Called(device, id, ipv4, ipv6, geo) return args.Get(0).(*openrtb.Device) } diff --git a/privacy/scrubber.go b/privacy/scrubber.go index 655436838e6..8771c8b3282 100644 --- a/privacy/scrubber.go +++ b/privacy/scrubber.go @@ -7,6 +7,17 @@ import ( "github.com/mxmCherry/openrtb" ) +// ScrubStrategyIPV4 defines the approach to scrub PII from an IPV4 address. +type ScrubStrategyIPV4 int + +const ( + // ScrubStrategyIPV4None does not remove any part of an IPV4 address. + ScrubStrategyIPV4None ScrubStrategyIPV4 = iota + + // ScrubStrategyIPV4Lowest8 zeroes out the last 8 bits of an IPV4 address. + ScrubStrategyIPV4Lowest8 +) + // ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address. type ScrubStrategyIPV6 int @@ -49,9 +60,20 @@ const ( ScrubStrategyUserID ) +// ScrubStrategyDeviceID defines the approach to remove hardware id and device id data. +type ScrubStrategyDeviceID int + +const ( + // ScrubStrategyDeviceIDNone does not remove hardware id and device id data. + ScrubStrategyDeviceIDNone ScrubStrategyDeviceID = iota + + // ScrubStrategyDeviceIDAll removes all hardware and device id data (ifa, mac hashes device id hashes) + ScrubStrategyDeviceIDAll +) + // Scrubber removes PII from parts of an OpenRTB request. type Scrubber interface { - ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device + ScrubDevice(device *openrtb.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb.User } @@ -62,20 +84,28 @@ func NewScrubber() Scrubber { return scrubber{} } -func (scrubber) ScrubDevice(device *openrtb.Device, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { +func (scrubber) ScrubDevice(device *openrtb.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb.Device { if device == nil { return nil } deviceCopy := *device - deviceCopy.DIDMD5 = "" - deviceCopy.DIDSHA1 = "" - deviceCopy.DPIDMD5 = "" - deviceCopy.DPIDSHA1 = "" - deviceCopy.IFA = "" - deviceCopy.MACMD5 = "" - deviceCopy.MACSHA1 = "" - deviceCopy.IP = scrubIPV4(device.IP) + + switch id { + case ScrubStrategyDeviceIDAll: + deviceCopy.DIDMD5 = "" + deviceCopy.DIDSHA1 = "" + deviceCopy.DPIDMD5 = "" + deviceCopy.DPIDSHA1 = "" + deviceCopy.IFA = "" + deviceCopy.MACMD5 = "" + deviceCopy.MACSHA1 = "" + } + + switch ipv4 { + case ScrubStrategyIPV4Lowest8: + deviceCopy.IP = scrubIPV4Lowest8(device.IP) + } switch ipv6 { case ScrubStrategyIPV6Lowest16: @@ -124,7 +154,7 @@ func (scrubber) ScrubUser(user *openrtb.User, strategy ScrubStrategyUser, geo Sc return &userCopy } -func scrubIPV4(ip string) string { +func scrubIPV4Lowest8(ip string) string { i := strings.LastIndex(ip, ".") if i == -1 { return "" diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go index 67241019317..e0a2cb86f64 100644 --- a/privacy/scrubber_test.go +++ b/privacy/scrubber_test.go @@ -31,28 +31,21 @@ func TestScrubDevice(t *testing.T) { testCases := []struct { description string expected *openrtb.Device + id ScrubStrategyDeviceID + ipv4 ScrubStrategyIPV4 ipv6 ScrubStrategyIPV6 geo ScrubStrategyGeo }{ { - description: "IPv6 Lowest 32 & Geo Full", - expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", - Geo: &openrtb.Geo{}, - }, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoFull, + description: "All Strageties - None", + expected: device, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4None, + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoNone, }, { - description: "IPv6 Lowest 16 & Geo Full", + description: "All Strageties - Strictest", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -62,14 +55,16 @@ func TestScrubDevice(t *testing.T) { MACMD5: "", IFA: "", IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", + IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", Geo: &openrtb.Geo{}, }, - ipv6: ScrubStrategyIPV6Lowest16, + id: ScrubStrategyDeviceIDAll, + ipv4: ScrubStrategyIPV4Lowest8, + ipv6: ScrubStrategyIPV6Lowest32, geo: ScrubStrategyGeoFull, }, { - description: "IPv6 None & Geo Full", + description: "Isolated - ID - All", expected: &openrtb.Device{ DIDMD5: "", DIDSHA1: "", @@ -78,161 +73,126 @@ func TestScrubDevice(t *testing.T) { MACSHA1: "", MACMD5: "", IFA: "", - IP: "1.2.3.0", + IP: "1.2.3.4", IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", - Geo: &openrtb.Geo{}, + Geo: device.Geo, }, + id: ScrubStrategyDeviceIDAll, + ipv4: ScrubStrategyIPV4None, ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoFull, + geo: ScrubStrategyGeoNone, }, { - description: "IPv6 Lowest 32 & Geo Reduced", + description: "Isolated - IPv4 - Lowest 8", expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", + DIDMD5: "anyDIDMD5", + DIDSHA1: "anyDIDSHA1", + DPIDMD5: "anyDPIDMD5", + DPIDSHA1: "anyDPIDSHA1", + MACSHA1: "anyMACSHA1", + MACMD5: "anyMACMD5", + IFA: "anyIFA", IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", - Geo: &openrtb.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", + Geo: device.Geo, }, - ipv6: ScrubStrategyIPV6Lowest32, - geo: ScrubStrategyGeoReducedPrecision, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4Lowest8, + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoNone, }, { - description: "IPv6 Lowest 16 & Geo Reduced", + description: "Isolated - IPv6 - Lowest 16", expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", + DIDMD5: "anyDIDMD5", + DIDSHA1: "anyDIDSHA1", + DPIDMD5: "anyDPIDMD5", + DPIDSHA1: "anyDPIDSHA1", + MACSHA1: "anyMACSHA1", + MACMD5: "anyMACMD5", + IFA: "anyIFA", + IP: "1.2.3.4", IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", - Geo: &openrtb.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, + Geo: device.Geo, }, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4None, ipv6: ScrubStrategyIPV6Lowest16, - geo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "IPv6 None & Geo Reduced", - expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", - Geo: &openrtb.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoReducedPrecision, + geo: ScrubStrategyGeoNone, }, { - description: "IPv6 Lowest 32 & Geo None", + description: "Isolated - IPv6 - Lowest 32", expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", + DIDMD5: "anyDIDMD5", + DIDSHA1: "anyDIDSHA1", + DPIDMD5: "anyDPIDMD5", + DPIDSHA1: "anyDPIDSHA1", + MACSHA1: "anyMACSHA1", + MACMD5: "anyMACMD5", + IFA: "anyIFA", + IP: "1.2.3.4", IPv6: "2001:0db8:0000:0000:0000:ff00:0:0", - Geo: &openrtb.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, + Geo: device.Geo, }, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4None, ipv6: ScrubStrategyIPV6Lowest32, geo: ScrubStrategyGeoNone, }, { - description: "IPv6 Lowest 16 & Geo None", + description: "Isolated - Geo - Reduced Precision", expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", - IPv6: "2001:0db8:0000:0000:0000:ff00:0042:0", + DIDMD5: "anyDIDMD5", + DIDSHA1: "anyDIDSHA1", + DPIDMD5: "anyDPIDMD5", + DPIDSHA1: "anyDPIDSHA1", + MACSHA1: "anyMACSHA1", + MACMD5: "anyMACMD5", + IFA: "anyIFA", + IP: "1.2.3.4", + IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", Geo: &openrtb.Geo{ - Lat: 123.456, + Lat: 123.46, Lon: 678.89, Metro: "some metro", City: "some city", ZIP: "some zip", }, }, - ipv6: ScrubStrategyIPV6Lowest16, - geo: ScrubStrategyGeoNone, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4None, + ipv6: ScrubStrategyIPV6None, + geo: ScrubStrategyGeoReducedPrecision, }, { - description: "IPv6 None & Geo None", + description: "Isolated - Geo - Full", expected: &openrtb.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", + DIDMD5: "anyDIDMD5", + DIDSHA1: "anyDIDSHA1", + DPIDMD5: "anyDPIDMD5", + DPIDSHA1: "anyDPIDSHA1", + MACSHA1: "anyMACSHA1", + MACMD5: "anyMACMD5", + IFA: "anyIFA", + IP: "1.2.3.4", IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329", - Geo: &openrtb.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, + Geo: &openrtb.Geo{}, }, + id: ScrubStrategyDeviceIDNone, + ipv4: ScrubStrategyIPV4None, ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, + geo: ScrubStrategyGeoFull, }, } for _, test := range testCases { - result := NewScrubber().ScrubDevice(device, test.ipv6, test.geo) + result := NewScrubber().ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo) assert.Equal(t, test.expected, result, test.description) } } func TestScrubDeviceNil(t *testing.T) { - result := NewScrubber().ScrubDevice(nil, ScrubStrategyIPV6None, ScrubStrategyGeoNone) + result := NewScrubber().ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone) assert.Nil(t, result) } @@ -458,7 +418,7 @@ func TestScrubIPV4(t *testing.T) { } for _, test := range testCases { - result := scrubIPV4(test.IP) + result := scrubIPV4Lowest8(test.IP) assert.Equal(t, test.cleanedIP, result, test.description) } } From d3ba8a94e3830af798b26a5c97c10ff0d94b44de Mon Sep 17 00:00:00 2001 From: Bill Newman Date: Tue, 15 Sep 2020 17:21:59 +0300 Subject: [PATCH 199/318] New colossus adapter [Clean branch] (#1495) Co-authored-by: Aiholkin --- adapters/colossus/colossus.go | 137 ++++++++++++++++++ adapters/colossus/colossus_test.go | 12 ++ .../colossustest/exemplary/simple-banner.json | 132 +++++++++++++++++ .../colossustest/exemplary/simple-video.json | 119 +++++++++++++++ .../exemplary/simple-web-banner.json | 133 +++++++++++++++++ .../colossus/colossustest/params/banner.json | 3 + .../colossustest/params/race/banner.json | 3 + .../colossustest/params/race/video.json | 3 + .../colossus/colossustest/params/video.json | 3 + .../supplemental/bad-imp-ext.json | 42 ++++++ .../supplemental/bad_response.json | 85 +++++++++++ .../supplemental/bad_status_code.json | 79 ++++++++++ .../supplemental/empty_imp_ext.json | 38 +++++ .../supplemental/imp_ext_empty_object.json | 39 +++++ .../supplemental/imp_ext_string.json | 39 +++++ .../colossustest/supplemental/status-204.json | 79 ++++++++++ .../colossustest/supplemental/status-404.json | 85 +++++++++++ .../supplemental/string_imp_ext.json | 39 +++++ adapters/colossus/params_test.go | 46 ++++++ adapters/colossus/usersync.go | 13 ++ adapters/colossus/usersync_test.go | 35 +++++ config/config.go | 2 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_colossus.go | 6 + static/bidder-info/colossus.yaml | 11 ++ static/bidder-params/colossus.json | 14 ++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 29 files changed, 1204 insertions(+) create mode 100644 adapters/colossus/colossus.go create mode 100644 adapters/colossus/colossus_test.go create mode 100644 adapters/colossus/colossustest/exemplary/simple-banner.json create mode 100644 adapters/colossus/colossustest/exemplary/simple-video.json create mode 100644 adapters/colossus/colossustest/exemplary/simple-web-banner.json create mode 100644 adapters/colossus/colossustest/params/banner.json create mode 100644 adapters/colossus/colossustest/params/race/banner.json create mode 100644 adapters/colossus/colossustest/params/race/video.json create mode 100644 adapters/colossus/colossustest/params/video.json create mode 100644 adapters/colossus/colossustest/supplemental/bad-imp-ext.json create mode 100644 adapters/colossus/colossustest/supplemental/bad_response.json create mode 100644 adapters/colossus/colossustest/supplemental/bad_status_code.json create mode 100644 adapters/colossus/colossustest/supplemental/empty_imp_ext.json create mode 100644 adapters/colossus/colossustest/supplemental/imp_ext_empty_object.json create mode 100644 adapters/colossus/colossustest/supplemental/imp_ext_string.json create mode 100644 adapters/colossus/colossustest/supplemental/status-204.json create mode 100644 adapters/colossus/colossustest/supplemental/status-404.json create mode 100644 adapters/colossus/colossustest/supplemental/string_imp_ext.json create mode 100644 adapters/colossus/params_test.go create mode 100644 adapters/colossus/usersync.go create mode 100644 adapters/colossus/usersync_test.go create mode 100644 openrtb_ext/imp_colossus.go create mode 100644 static/bidder-info/colossus.yaml create mode 100644 static/bidder-params/colossus.json diff --git a/adapters/colossus/colossus.go b/adapters/colossus/colossus.go new file mode 100644 index 00000000000..89cd49d2881 --- /dev/null +++ b/adapters/colossus/colossus.go @@ -0,0 +1,137 @@ +package colossus + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/buger/jsonparser" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type ColossusAdapter struct { + URI string +} + +// NewColossusBidder Initializes the Bidder +func NewColossusBidder(endpoint string) *ColossusAdapter { + return &ColossusAdapter{ + URI: endpoint, + } +} + +// MakeRequests create bid request for colossus demand +func (a *ColossusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var err error + var tagID string + + var adapterRequests []*adapters.RequestData + + reqCopy := *request + for _, imp := range request.Imp { + reqCopy.Imp = []openrtb.Imp{imp} + + tagID, err = jsonparser.GetString(reqCopy.Imp[0].Ext, "bidder", "TagID") + if err != nil { + errs = append(errs, err) + continue + } + + reqCopy.Imp[0].TagID = tagID + + adapterReq, errors := a.makeRequest(&reqCopy) + if adapterReq != nil { + adapterRequests = append(adapterRequests, adapterReq) + } + errs = append(errs, errors...) + } + return adapterRequests, errs +} + +func (a *ColossusAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) { + + var errs []error + + reqJSON, err := json.Marshal(request) + + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + return &adapters.RequestData{ + Method: "POST", + Uri: a.URI, + Body: reqJSON, + Headers: headers, + }, errs +} + +// MakeBids makes the bids +func (a *ColossusAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusNotFound { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) (openrtb_ext.BidType, error) { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner == nil && imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } + return mediaType, nil + } + } + + // This shouldnt happen. Lets handle it just incase by returning an error. + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID), + } +} diff --git a/adapters/colossus/colossus_test.go b/adapters/colossus/colossus_test.go new file mode 100644 index 00000000000..f4fd12f3fab --- /dev/null +++ b/adapters/colossus/colossus_test.go @@ -0,0 +1,12 @@ +package colossus + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + colossusAdapter := NewColossusBidder("http://example.com/?c=o&m=rtb") + adapterstest.RunJSONBidderTest(t, "colossustest", colossusAdapter) +} diff --git a/adapters/colossus/colossustest/exemplary/simple-banner.json b/adapters/colossus/colossustest/exemplary/simple-banner.json new file mode 100644 index 00000000000..1adc7010ed8 --- /dev/null +++ b/adapters/colossus/colossustest/exemplary/simple-banner.json @@ -0,0 +1,132 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": { + "bidder": { + "TagID": "61317" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": { + "bidder": { + "TagID": "61317" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/colossus/colossustest/exemplary/simple-video.json b/adapters/colossus/colossustest/exemplary/simple-video.json new file mode 100644 index 00000000000..78516fcef31 --- /dev/null +++ b/adapters/colossus/colossustest/exemplary/simple-video.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "TagID": "61318" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "tagid": "61318", + "ext": { + "bidder": { + "TagID": "61318" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "colossus" + } + ], + "cur": "USD" + } + } + } + ], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/colossus/colossustest/exemplary/simple-web-banner.json b/adapters/colossus/colossustest/exemplary/simple-web-banner.json new file mode 100644 index 00000000000..37baf3d97dd --- /dev/null +++ b/adapters/colossus/colossustest/exemplary/simple-web-banner.json @@ -0,0 +1,133 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "1", + "ext": { + "bidder": { + "TagID": "1" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "colossus" + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/colossus/colossustest/params/banner.json b/adapters/colossus/colossustest/params/banner.json new file mode 100644 index 00000000000..7c2643d4901 --- /dev/null +++ b/adapters/colossus/colossustest/params/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "61317" +} diff --git a/adapters/colossus/colossustest/params/race/banner.json b/adapters/colossus/colossustest/params/race/banner.json new file mode 100644 index 00000000000..7c2643d4901 --- /dev/null +++ b/adapters/colossus/colossustest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "61317" +} diff --git a/adapters/colossus/colossustest/params/race/video.json b/adapters/colossus/colossustest/params/race/video.json new file mode 100644 index 00000000000..56f865c71d9 --- /dev/null +++ b/adapters/colossus/colossustest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "61318" +} diff --git a/adapters/colossus/colossustest/params/video.json b/adapters/colossus/colossustest/params/video.json new file mode 100644 index 00000000000..56f865c71d9 --- /dev/null +++ b/adapters/colossus/colossustest/params/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "61318" +} diff --git a/adapters/colossus/colossustest/supplemental/bad-imp-ext.json b/adapters/colossus/colossustest/supplemental/bad-imp-ext.json new file mode 100644 index 00000000000..13656337e5e --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/bad-imp-ext.json @@ -0,0 +1,42 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": { + "colossus": { + "TagID": "61317" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, +"expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } +] +} diff --git a/adapters/colossus/colossustest/supplemental/bad_response.json b/adapters/colossus/colossustest/supplemental/bad_response.json new file mode 100644 index 00000000000..c69b00c8e6e --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/bad_response.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 200, + "body": "" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/colossus/colossustest/supplemental/bad_status_code.json b/adapters/colossus/colossustest/supplemental/bad_status_code.json new file mode 100644 index 00000000000..f5b6a5748af --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/bad_status_code.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": {} + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": {} + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/colossus/colossustest/supplemental/empty_imp_ext.json b/adapters/colossus/colossustest/supplemental/empty_imp_ext.json new file mode 100644 index 00000000000..00e1cf60fb7 --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/empty_imp_ext.json @@ -0,0 +1,38 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": {} + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/colossus/colossustest/supplemental/imp_ext_empty_object.json b/adapters/colossus/colossustest/supplemental/imp_ext_empty_object.json new file mode 100644 index 00000000000..e9c1f257aba --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/imp_ext_empty_object.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": {} + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/colossus/colossustest/supplemental/imp_ext_string.json b/adapters/colossus/colossustest/supplemental/imp_ext_string.json new file mode 100644 index 00000000000..362a8fa4df8 --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/imp_ext_string.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": "" + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/colossus/colossustest/supplemental/status-204.json b/adapters/colossus/colossustest/supplemental/status-204.json new file mode 100644 index 00000000000..73f8bc71f23 --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/status-204.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "17", + "ext": { + "bidder": { + "TagID": "17" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }] +} diff --git a/adapters/colossus/colossustest/supplemental/status-404.json b/adapters/colossus/colossustest/supplemental/status-404.json new file mode 100644 index 00000000000..676eb8bb2f4 --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/status-404.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://example.com/?c=o&m=rtb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "100000000", + "ext": { + "bidder": { + "TagID": "100000000" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 404, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 404. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/colossus/colossustest/supplemental/string_imp_ext.json b/adapters/colossus/colossustest/supplemental/string_imp_ext.json new file mode 100644 index 00000000000..362a8fa4df8 --- /dev/null +++ b/adapters/colossus/colossustest/supplemental/string_imp_ext.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "tagid": "61317", + "ext": "" + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Key path not found", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/colossus/params_test.go b/adapters/colossus/params_test.go new file mode 100644 index 00000000000..2883de2f53e --- /dev/null +++ b/adapters/colossus/params_test.go @@ -0,0 +1,46 @@ +package colossus + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// TestValidParams makes sure that the colossus schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderColossus, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected colossus params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the colossus schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderColossus, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"TagID": "61317"}`, +} + +var invalidParams = []string{ + `{"id": "123"}`, + `{"tagid": "123"}`, + `{"TagID": 16}`, +} diff --git a/adapters/colossus/usersync.go b/adapters/colossus/usersync.go new file mode 100644 index 00000000000..a4e82ee3bde --- /dev/null +++ b/adapters/colossus/usersync.go @@ -0,0 +1,13 @@ +package colossus + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +// NewColossusSyncer returns colossus syncer +func NewColossusSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("colossus", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/colossus/usersync_test.go b/adapters/colossus/usersync_test.go new file mode 100644 index 00000000000..79d5483d528 --- /dev/null +++ b/adapters/colossus/usersync_test.go @@ -0,0 +1,35 @@ +package colossus + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestColossusSyncer(t *testing.T) { + syncURL := "https://sync.colossusssp.com/pbs.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dcolossus%26uid%3D%5BUID%5D" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewColossusSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + Consent: "A", + }, + CCPA: ccpa.Policy{ + Value: "1-YY", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://sync.colossusssp.com/pbs.gif?gdpr=0&gdpr_consent=A&us_privacy=1-YY&redir=http%3A%2F%2Flocalhost%3A8000%2Fsetuid%3Fbidder%3Dcolossus%26uid%3D%5BUID%5D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 59ba55ebe26..5731b65d567 100755 --- a/config/config.go +++ b/config/config.go @@ -693,6 +693,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeintoo, "https://ib.beintoo.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeintoo%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderColossus, "https://sync.colossusssp.com/pbs.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcolossus%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConsumable, "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconsumable%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -911,6 +912,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um") v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs") + v.SetDefault("adapters.colossus.endpoint", "http://colossusssp.com/?c=o&m=rtb") v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2") v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25") v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index a160e87aad7..5bb788b63b9 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -31,6 +31,7 @@ import ( "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" + "github.com/prebid/prebid-server/adapters/colossus" "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/cpmstar" @@ -118,6 +119,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo), openrtb_ext.BidderBeintoo: beintoo.NewBeintooBidder(cfg.Adapters[string(openrtb_ext.BidderBeintoo)].Endpoint), openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBrightroll)].ExtraAdapterInfo), + openrtb_ext.BidderColossus: colossus.NewColossusBidder(cfg.Adapters[string(openrtb_ext.BidderColossus)].Endpoint), openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), openrtb_ext.BidderCpmstar: cpmstar.NewCpmstarBidder(cfg.Adapters[string(openrtb_ext.BidderCpmstar)].Endpoint), openrtb_ext.BidderDatablocks: datablocks.NewDatablocksBidder(cfg.Adapters[string(openrtb_ext.BidderDatablocks)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 876eeab86bd..221f97c9697 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -49,6 +49,7 @@ const ( BidderBeachfront BidderName = "beachfront" BidderBeintoo BidderName = "beintoo" BidderBrightroll BidderName = "brightroll" + BidderColossus BidderName = "colossus" BidderConsumable BidderName = "consumable" BidderConversant BidderName = "conversant" BidderCpmstar BidderName = "cpmstar" @@ -133,6 +134,7 @@ var BidderMap = map[string]BidderName{ "beachfront": BidderBeachfront, "beintoo": BidderBeintoo, "brightroll": BidderBrightroll, + "colossus": BidderColossus, "consumable": BidderConsumable, "conversant": BidderConversant, "cpmstar": BidderCpmstar, diff --git a/openrtb_ext/imp_colossus.go b/openrtb_ext/imp_colossus.go new file mode 100644 index 00000000000..8969000558d --- /dev/null +++ b/openrtb_ext/imp_colossus.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpColossus defines colossus specifiec param +type ExtImpColossus struct { + TagID string `json:"TagID"` +} diff --git a/static/bidder-info/colossus.yaml b/static/bidder-info/colossus.yaml new file mode 100644 index 00000000000..901c824c603 --- /dev/null +++ b/static/bidder-info/colossus.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "support@huddledmasses.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/colossus.json b/static/bidder-params/colossus.json new file mode 100644 index 00000000000..f2732fa0854 --- /dev/null +++ b/static/bidder-params/colossus.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Colossus Adapter Params", + "description": "A schema which validates params accepted by the Colossus adapter", + + "type": "object", + "properties": { + "TagID": { + "type": "string", + "description": "An ID which identifies the colossus ad tag" + } + }, + "required" : [ "TagID" ] + } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 89540ea205b..c6ae984efc9 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -23,6 +23,7 @@ import ( "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" + "github.com/prebid/prebid-server/adapters/colossus" "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/cpmstar" @@ -99,6 +100,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderBeachfront, beachfront.NewBeachfrontSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeintoo, beintoo.NewBeintooSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBrightroll, brightroll.NewBrightrollSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderColossus, colossus.NewColossusSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderConsumable, consumable.NewConsumableSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderConversant, conversant.NewConversantSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderCpmstar, cpmstar.NewCpmstarSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 9197ed9507d..2cf0b2513c5 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -31,6 +31,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderBeachfront): syncConfig, string(openrtb_ext.BidderBeintoo): syncConfig, string(openrtb_ext.BidderBrightroll): syncConfig, + string(openrtb_ext.BidderColossus): syncConfig, string(openrtb_ext.BidderConsumable): syncConfig, string(openrtb_ext.BidderConversant): syncConfig, string(openrtb_ext.BidderCpmstar): syncConfig, From 7b59a4bd49e502b05f81624bcc908259aec757cf Mon Sep 17 00:00:00 2001 From: Daniel Lawrence Date: Tue, 15 Sep 2020 09:12:57 -0700 Subject: [PATCH 200/318] New: InMobi Prebid Server Adapter (#1489) * Adding InMobi adapter * code review feedback, also explicitly working with Imp[0], as we don't support multiple impressions * less tolerant bidder params due to sneaky 1.13 -> 1.14+ change --- adapters/inmobi/inmobi.go | 127 ++++++++++++++++++ adapters/inmobi/inmobi_test.go | 10 ++ .../inmobitest/exemplary/simple-banner.json | 107 +++++++++++++++ .../inmobitest/exemplary/simple-video.json | 109 +++++++++++++++ .../inmobi/inmobitest/params/race/banner.json | 3 + .../inmobi/inmobitest/params/race/video.json | 3 + .../inmobi/inmobitest/supplemental/204.json | 61 +++++++++ .../inmobi/inmobitest/supplemental/400.json | 67 +++++++++ .../supplemental/banner-format-coersion.json | 113 ++++++++++++++++ .../supplemental/ext-unmarshal-err.json | 28 ++++ .../supplemental/missing-plc-error.json | 28 ++++ .../inmobitest/supplemental/no-imp-error.json | 13 ++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_inmobi.go | 5 + static/bidder-info/inmobi.yaml | 8 ++ static/bidder-params/inmobi.json | 13 ++ usersync/usersyncers/syncer_test.go | 1 + 19 files changed, 701 insertions(+) create mode 100644 adapters/inmobi/inmobi.go create mode 100644 adapters/inmobi/inmobi_test.go create mode 100644 adapters/inmobi/inmobitest/exemplary/simple-banner.json create mode 100644 adapters/inmobi/inmobitest/exemplary/simple-video.json create mode 100644 adapters/inmobi/inmobitest/params/race/banner.json create mode 100644 adapters/inmobi/inmobitest/params/race/video.json create mode 100644 adapters/inmobi/inmobitest/supplemental/204.json create mode 100644 adapters/inmobi/inmobitest/supplemental/400.json create mode 100644 adapters/inmobi/inmobitest/supplemental/banner-format-coersion.json create mode 100644 adapters/inmobi/inmobitest/supplemental/ext-unmarshal-err.json create mode 100644 adapters/inmobi/inmobitest/supplemental/missing-plc-error.json create mode 100644 adapters/inmobi/inmobitest/supplemental/no-imp-error.json create mode 100644 openrtb_ext/imp_inmobi.go create mode 100644 static/bidder-info/inmobi.yaml create mode 100644 static/bidder-params/inmobi.json diff --git a/adapters/inmobi/inmobi.go b/adapters/inmobi/inmobi.go new file mode 100644 index 00000000000..4d46ffb8f1e --- /dev/null +++ b/adapters/inmobi/inmobi.go @@ -0,0 +1,127 @@ +package inmobi + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" +) + +type InMobiAdapter struct { + endPoint string +} + +func NewInMobiAdapter(endpoint string) *InMobiAdapter { + return &InMobiAdapter{ + endPoint: endpoint, + } +} + +func (a *InMobiAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impression in the request", + }} + } + + if err := preprocess(&request.Imp[0]); err != nil { + errs = append(errs, err) + return nil, errs + } + + reqJson, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.endPoint, + Body: reqJson, + Headers: headers, + }}, errs +} + +func (a *InMobiAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected http status code: %d", response.StatusCode), + }} + } + + var serverBidResponse openrtb.BidResponse + if err := json.Unmarshal(response.Body, &serverBidResponse); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range serverBidResponse.SeatBid { + for i := range sb.Bid { + mediaType := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: mediaType, + }) + } + } + + return bidResponse, nil +} + +func preprocess(imp *openrtb.Imp) error { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + + var inMobiExt openrtb_ext.ExtImpInMobi + if err := json.Unmarshal(bidderExt.Bidder, &inMobiExt); err != nil { + return &errortypes.BadInput{Message: "bad InMobi bidder ext"} + } + + if len(inMobiExt.Plc) == 0 { + return &errortypes.BadInput{Message: "'plc' is a required attribute for InMobi's bidder ext"} + } + + if imp.Banner != nil { + banner := *imp.Banner + imp.Banner = &banner + if (banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0) && len(banner.Format) > 0 { + format := banner.Format[0] + banner.W = &format.W + banner.H = &format.H + } + } + + return nil +} + +func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impId { + if imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } + break + } + } + return mediaType +} diff --git a/adapters/inmobi/inmobi_test.go b/adapters/inmobi/inmobi_test.go new file mode 100644 index 00000000000..6aa58d97222 --- /dev/null +++ b/adapters/inmobi/inmobi_test.go @@ -0,0 +1,10 @@ +package inmobi + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "inmobitest", NewInMobiAdapter("https://api.w.inmobi.com/showad/openrtb/bidder/prebid")) +} diff --git a/adapters/inmobi/inmobitest/exemplary/simple-banner.json b/adapters/inmobi/inmobitest/exemplary/simple-banner.json new file mode 100644 index 00000000000..4345ef8ff66 --- /dev/null +++ b/adapters/inmobi/inmobitest/exemplary/simple-banner.json @@ -0,0 +1,107 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://api.w.inmobi.com/showad/openrtb/bidder/prebid", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "req-id", + "seatbid": [ + { + "bid": [ + { + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + }, + "nurl": "https://some.event.url/params", + "crid": "123456789", + "adomain": [], + "price": 2.0, + "id": "1234", + "adm": "bannerhtml", + "impid": "imp-id" + } + ] + } + ] + } + } + }], + + "expectedBidResponses": [{ + "currency": "USD", + "bids": [{ + "bid": { + "id": "1234", + "impid": "imp-id", + "price": 2.0, + "adm": "bannerhtml", + "crid": "123456789", + "nurl": "https://some.event.url/params", + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + } + }, + "type": "banner" + }] + }] +} diff --git a/adapters/inmobi/inmobitest/exemplary/simple-video.json b/adapters/inmobi/inmobitest/exemplary/simple-video.json new file mode 100644 index 00000000000..20b3c0cc810 --- /dev/null +++ b/adapters/inmobi/inmobitest/exemplary/simple-video.json @@ -0,0 +1,109 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1598991608990" + } + }, + "video": { + "w": 640, + "h": 360, + "mimes": ["video/mp4"] + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://api.w.inmobi.com/showad/openrtb/bidder/prebid", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1598991608990" + } + }, + "video": { + "w": 640, + "h": 360, + "mimes": ["video/mp4"] + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "req-id", + "seatbid": [ + { + "bid": [ + { + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + }, + "nurl": "https://some.event.url/params", + "crid": "123456789", + "adomain": [], + "price": 2.0, + "id": "1234", + "adm": " ", + "impid": "imp-id" + } + ] + } + ] + } + } + }], + + "expectedBidResponses": [{ + "currency": "USD", + "bids": [{ + "bid": { + "id": "1234", + "impid": "imp-id", + "price": 2.0, + "adm": " ", + "crid": "123456789", + "nurl": "https://some.event.url/params", + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + } + }, + "type": "video" + }] + }] +} diff --git a/adapters/inmobi/inmobitest/params/race/banner.json b/adapters/inmobi/inmobitest/params/race/banner.json new file mode 100644 index 00000000000..7791393fc99 --- /dev/null +++ b/adapters/inmobi/inmobitest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "plc": "1596825400965" +} diff --git a/adapters/inmobi/inmobitest/params/race/video.json b/adapters/inmobi/inmobitest/params/race/video.json new file mode 100644 index 00000000000..74a44b6e6f9 --- /dev/null +++ b/adapters/inmobi/inmobitest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "plc": "1598991608990" +} diff --git a/adapters/inmobi/inmobitest/supplemental/204.json b/adapters/inmobi/inmobitest/supplemental/204.json new file mode 100644 index 00000000000..c811763678c --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/204.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://api.w.inmobi.com/showad/openrtb/bidder/prebid", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }] +} diff --git a/adapters/inmobi/inmobitest/supplemental/400.json b/adapters/inmobi/inmobitest/supplemental/400.json new file mode 100644 index 00000000000..2df5c85aaca --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/400.json @@ -0,0 +1,67 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://api.w.inmobi.com/showad/openrtb/bidder/prebid", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected http status code: 400", + "comparison": "literal" + } + ] +} diff --git a/adapters/inmobi/inmobitest/supplemental/banner-format-coersion.json b/adapters/inmobi/inmobitest/supplemental/banner-format-coersion.json new file mode 100644 index 00000000000..514f86817c9 --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/banner-format-coersion.json @@ -0,0 +1,113 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "device-ifa", + "ip": "1.1.1.1", + "ua": "device-ua" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "format": [{ + "w": 320, + "h": 50 + }] + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://api.w.inmobi.com/showad/openrtb/bidder/prebid", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "device-ifa", + "ip": "1.1.1.1", + "ua": "device-ua" + }, + "imp": [ + { + "ext": { + "bidder": { + "plc": "1596825400965" + } + }, + "banner": { + "format": [{ + "w": 320, + "h": 50 + }], + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "req-id", + "seatbid": [ + { + "bid": [ + { + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + }, + "nurl": "https://some.event.url/params", + "crid": "123456789", + "adomain": [], + "price": 2.0, + "id": "1234", + "adm": "bannerhtml", + "impid": "imp-id" + } + ] + } + ] + } + } + }], + + "expectedBidResponses": [{ + "currency": "USD", + "bids": [{ + "bid": { + "id": "1234", + "impid": "imp-id", + "price": 2.0, + "adm": "bannerhtml", + "crid": "123456789", + "nurl": "https://some.event.url/params", + "ext": { + "prebid": { + "meta": { + "networkName": "inmobi" + } + } + } + }, + "type": "banner" + }] + }] +} diff --git a/adapters/inmobi/inmobitest/supplemental/ext-unmarshal-err.json b/adapters/inmobi/inmobitest/supplemental/ext-unmarshal-err.json new file mode 100644 index 00000000000..957a2f6f952 --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/ext-unmarshal-err.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "req-id", + "imp": [ + { + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "plc": true + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "bad InMobi bidder ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/inmobi/inmobitest/supplemental/missing-plc-error.json b/adapters/inmobi/inmobitest/supplemental/missing-plc-error.json new file mode 100644 index 00000000000..52697cf90c6 --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/missing-plc-error.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "req-id", + "imp": [ + { + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "a": 1 + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "'plc' is a required attribute for InMobi's bidder ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/inmobi/inmobitest/supplemental/no-imp-error.json b/adapters/inmobi/inmobitest/supplemental/no-imp-error.json new file mode 100644 index 00000000000..6c6c363425a --- /dev/null +++ b/adapters/inmobi/inmobitest/supplemental/no-imp-error.json @@ -0,0 +1,13 @@ +{ + "mockBidRequest": { + "id": "req-id", + "imp": [ + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "No impression in the request", + "comparison": "literal" + } + ] +} diff --git a/config/config.go b/config/config.go index 5731b65d567..53daf117fdf 100755 --- a/config/config.go +++ b/config/config.go @@ -926,6 +926,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid") v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs") + v.SetDefault("adapters.inmobi.endpoint", "https://api.w.inmobi.com/showad/openrtb/bidder/prebid") v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932") v.SetDefault("adapters.kidoz.endpoint", "http://prebid-adapter.kidoz.net/openrtb2/auction?src=prebid-server") v.SetDefault("adapters.kubient.endpoint", "https://kssp.kbntx.ch/prebid") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 5bb788b63b9..d428168921a 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -45,6 +45,7 @@ import ( "github.com/prebid/prebid-server/adapters/grid" "github.com/prebid/prebid-server/adapters/gumgum" "github.com/prebid/prebid-server/adapters/improvedigital" + "github.com/prebid/prebid-server/adapters/inmobi" "github.com/prebid/prebid-server/adapters/ix" "github.com/prebid/prebid-server/adapters/kidoz" "github.com/prebid/prebid-server/adapters/kubient" @@ -135,6 +136,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint), openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint), + openrtb_ext.BidderInMobi: inmobi.NewInMobiAdapter(cfg.Adapters[string(openrtb_ext.BidderInMobi)].Endpoint), openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint), openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint), openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 221f97c9697..dcfd663ebc7 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -64,6 +64,7 @@ const ( BidderGrid BidderName = "grid" BidderGumGum BidderName = "gumgum" BidderImprovedigital BidderName = "improvedigital" + BidderInMobi BidderName = "inmobi" BidderIx BidderName = "ix" BidderKidoz BidderName = "kidoz" BidderKubient BidderName = "kubient" @@ -149,6 +150,7 @@ var BidderMap = map[string]BidderName{ "grid": BidderGrid, "gumgum": BidderGumGum, "improvedigital": BidderImprovedigital, + "inmobi": BidderInMobi, "ix": BidderIx, "kidoz": BidderKidoz, "kubient": BidderKubient, diff --git a/openrtb_ext/imp_inmobi.go b/openrtb_ext/imp_inmobi.go new file mode 100644 index 00000000000..d74e3cac8b0 --- /dev/null +++ b/openrtb_ext/imp_inmobi.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpInMobi struct { + Plc string `json:"plc"` +} diff --git a/static/bidder-info/inmobi.yaml b/static/bidder-info/inmobi.yaml new file mode 100644 index 00000000000..3f8cdd8cb91 --- /dev/null +++ b/static/bidder-info/inmobi.yaml @@ -0,0 +1,8 @@ +maintainer: + email: "prebid-support@inmobi.com" + +capabilities: + app: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/inmobi.json b/static/bidder-params/inmobi.json new file mode 100644 index 00000000000..631b3137b72 --- /dev/null +++ b/static/bidder-params/inmobi.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "InMobi Adapter Params", + "description": "A schema which validates params accepted by the InMobi adapter", + "type": "object", + "properties": { + "plc": { + "type": ["string"], + "description": "An ID corresponding to the placement selling the impression" + } + }, + "required": ["plc"] +} diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 2cf0b2513c5..bd250489fdd 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -89,6 +89,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderAdhese: true, openrtb_ext.BidderAdoppler: true, openrtb_ext.BidderApplogy: true, + openrtb_ext.BidderInMobi: true, openrtb_ext.BidderKidoz: true, openrtb_ext.BidderKubient: true, openrtb_ext.BidderMobileFuse: true, From ab653bc8a3ea0e7bf814a1741a0c7eab2b1e5139 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Wed, 16 Sep 2020 18:04:48 -0400 Subject: [PATCH 201/318] Revert "Added new size 640x360 (Id: 198) (#1490)" (#1501) This reverts commit fa23f5c226df99a9a4ef318100fdb7d84d3e40fa. --- adapters/rubicon/rubicon.go | 1 - 1 file changed, 1 deletion(-) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 7d6e0e12039..56ae7b2f792 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -236,7 +236,6 @@ var rubiSizeMap = map[rubiSize]int{ {w: 800, h: 250}: 125, {w: 200, h: 600}: 126, {w: 640, h: 320}: 156, - {w: 640, h: 360}: 198, } // defines the contract for bidrequest.user.ext.eids[i].ext From f6624b7acc924f6e66b014825bf07d2edb072eb9 Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Thu, 17 Sep 2020 01:48:17 -0400 Subject: [PATCH 202/318] CCPA Publisher No Sale Relationships (#1465) --- adapters/33across/usersync_test.go | 2 +- adapters/adkernel/usersync_test.go | 2 +- adapters/adkernelAdn/usersync_test.go | 2 +- adapters/adman/usersync_test.go | 2 +- adapters/admixer/usersync_test.go | 7 +- adapters/adtarget/usersync_test.go | 5 +- adapters/aja/usersync_test.go | 5 +- adapters/avocet/usersync_test.go | 2 +- adapters/beachfront/usersync_test.go | 2 +- adapters/beintoo/usersync_test.go | 2 +- adapters/consumable/consumable.go | 13 +- adapters/consumable/usersync_test.go | 2 +- adapters/datablocks/usersync_test.go | 2 +- adapters/emx_digital/usersync_test.go | 2 +- adapters/engagebdr/usersync_test.go | 2 +- adapters/gamoshi/usersync_test.go | 2 +- adapters/gumgum/usersync_test.go | 2 +- adapters/improvedigital/usersync_test.go | 2 +- adapters/marsmedia/usersync_test.go | 2 +- adapters/nanointeractive/usersync_test.go | 24 +- adapters/pubmatic/usersync_test.go | 2 +- adapters/rhythmone/usersync_test.go | 2 +- adapters/sharethrough/butler.go | 15 +- adapters/smartadserver/usersync_test.go | 2 +- adapters/syncer.go | 2 +- adapters/syncer_test.go | 2 +- adapters/unruly/usersync_test.go | 2 +- adapters/valueimpression/usersync_test.go | 2 +- adapters/visx/usersync_test.go | 2 +- adapters/zeroclickfraud/usersync_test.go | 2 +- endpoints/auction.go | 6 +- endpoints/auction_test.go | 5 +- endpoints/cookie_sync.go | 50 +- endpoints/openrtb2/amp_auction.go | 41 +- endpoints/openrtb2/auction.go | 35 +- endpoints/openrtb2/auction_test.go | 48 ++ .../exchangetest/ccpa-nosale-any-bidder.json | 75 +++ .../ccpa-nosale-specific-bidder.json | 75 +++ exchange/utils.go | 70 +- exchange/utils_test.go | 80 ++- openrtb_ext/request.go | 5 + privacy/ccpa/consentwriter.go | 25 + privacy/ccpa/consentwriter_test.go | 51 ++ privacy/ccpa/parsedpolicy.go | 137 ++++ privacy/ccpa/parsedpolicy_test.go | 391 +++++++++++ privacy/ccpa/policy.go | 213 +++--- privacy/ccpa/policy_test.go | 630 +++++++++++------- privacy/enforcer.go | 43 ++ privacy/enforcer_test.go | 18 + privacy/gdpr/consentwriter.go | 44 ++ privacy/gdpr/consentwriter_test.go | 101 +++ privacy/gdpr/policy.go | 40 +- privacy/gdpr/policy_test.go | 113 +--- privacy/lmt/policy.go | 14 +- privacy/lmt/policy_test.go | 68 +- privacy/policies.go | 52 +- privacy/policies_test.go | 119 ---- privacy/writer.go | 18 + privacy/writer_test.go | 25 + 59 files changed, 1962 insertions(+), 747 deletions(-) create mode 100644 exchange/exchangetest/ccpa-nosale-any-bidder.json create mode 100644 exchange/exchangetest/ccpa-nosale-specific-bidder.json create mode 100644 privacy/ccpa/consentwriter.go create mode 100644 privacy/ccpa/consentwriter_test.go create mode 100644 privacy/ccpa/parsedpolicy.go create mode 100644 privacy/ccpa/parsedpolicy_test.go create mode 100644 privacy/enforcer.go create mode 100644 privacy/enforcer_test.go create mode 100644 privacy/gdpr/consentwriter.go create mode 100644 privacy/gdpr/consentwriter_test.go delete mode 100644 privacy/policies_test.go create mode 100644 privacy/writer.go create mode 100644 privacy/writer_test.go diff --git a/adapters/33across/usersync_test.go b/adapters/33across/usersync_test.go index a5e301b1082..a9eb4e57908 100644 --- a/adapters/33across/usersync_test.go +++ b/adapters/33across/usersync_test.go @@ -23,7 +23,7 @@ func Test33AcrossSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/adkernel/usersync_test.go b/adapters/adkernel/usersync_test.go index 0d539d11ee0..aeacf00b7f0 100644 --- a/adapters/adkernel/usersync_test.go +++ b/adapters/adkernel/usersync_test.go @@ -23,7 +23,7 @@ func TestAdkernelAdnSyncer(t *testing.T) { Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/adkernelAdn/usersync_test.go b/adapters/adkernelAdn/usersync_test.go index ecc759bdf70..92d688e6117 100644 --- a/adapters/adkernelAdn/usersync_test.go +++ b/adapters/adkernelAdn/usersync_test.go @@ -23,7 +23,7 @@ func TestAdkernelAdnSyncer(t *testing.T) { Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/adman/usersync_test.go b/adapters/adman/usersync_test.go index 55a6e2cec97..25da77db7ed 100644 --- a/adapters/adman/usersync_test.go +++ b/adapters/adman/usersync_test.go @@ -23,7 +23,7 @@ func TestAdmanSyncer(t *testing.T) { Consent: "ANDFJDS", }, CCPA: ccpa.Policy{ - Value: "1-YY", + Consent: "1-YY", }, }) diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go index a5715c64a46..d31f7b10fb1 100644 --- a/adapters/admixer/usersync_test.go +++ b/adapters/admixer/usersync_test.go @@ -1,12 +1,13 @@ package admixer import ( + "testing" + "text/template" + "github.com/prebid/prebid-server/privacy" "github.com/prebid/prebid-server/privacy/ccpa" "github.com/prebid/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" - "testing" - "text/template" ) func TestAdmixerSyncer(t *testing.T) { @@ -22,7 +23,7 @@ func TestAdmixerSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/adtarget/usersync_test.go b/adapters/adtarget/usersync_test.go index 3ab2ed5b5df..ddba9e7a720 100644 --- a/adapters/adtarget/usersync_test.go +++ b/adapters/adtarget/usersync_test.go @@ -2,10 +2,11 @@ package adtarget import ( "fmt" - "github.com/prebid/prebid-server/privacy/ccpa" "testing" "text/template" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy" "github.com/prebid/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" @@ -25,7 +26,7 @@ func TestAdtargetSyncer(t *testing.T) { Consent: "123", }, CCPA: ccpa.Policy{ - Value: "1-YY", + Consent: "1-YY", }, }) diff --git a/adapters/aja/usersync_test.go b/adapters/aja/usersync_test.go index dbb66cc9ae2..4b6c90ef141 100644 --- a/adapters/aja/usersync_test.go +++ b/adapters/aja/usersync_test.go @@ -1,10 +1,11 @@ package aja import ( - "github.com/prebid/prebid-server/privacy/ccpa" "testing" "text/template" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy" "github.com/prebid/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" @@ -23,7 +24,7 @@ func TestAJASyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go index 8fba403f1b1..3df39b77fce 100644 --- a/adapters/avocet/usersync_test.go +++ b/adapters/avocet/usersync_test.go @@ -23,7 +23,7 @@ func TestAvocetSyncer(t *testing.T) { Consent: "ConsentString", }, CCPA: ccpa.Policy{ - Value: "PrivacyString", + Consent: "PrivacyString", }, }) diff --git a/adapters/beachfront/usersync_test.go b/adapters/beachfront/usersync_test.go index 0267ac05eb7..db4d825eb5a 100644 --- a/adapters/beachfront/usersync_test.go +++ b/adapters/beachfront/usersync_test.go @@ -23,7 +23,7 @@ func TestBeachfrontSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/beintoo/usersync_test.go b/adapters/beintoo/usersync_test.go index 2cfca010226..880d6a84cee 100644 --- a/adapters/beintoo/usersync_test.go +++ b/adapters/beintoo/usersync_test.go @@ -23,7 +23,7 @@ func TestBeintooSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go index ff7451f15f7..18ece8d4c4a 100644 --- a/adapters/consumable/consumable.go +++ b/adapters/consumable/consumable.go @@ -3,15 +3,16 @@ package consumable import ( "encoding/json" "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/privacy/ccpa" - "net/http" - "net/url" - "strconv" - "strings" ) type ConsumableAdapter struct { @@ -136,9 +137,9 @@ func (a *ConsumableAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *a gdpr := bidGdpr{} - ccpaPolicy, err := ccpa.ReadPolicy(request) + ccpaPolicy, err := ccpa.ReadFromRequest(request) if err == nil { - body.CCPA = ccpaPolicy.Value + body.CCPA = ccpaPolicy.Consent } // TODO: Replace with gdpr.ReadPolicy when it is available diff --git a/adapters/consumable/usersync_test.go b/adapters/consumable/usersync_test.go index 017cb72975b..ef71c0b18c7 100644 --- a/adapters/consumable/usersync_test.go +++ b/adapters/consumable/usersync_test.go @@ -23,7 +23,7 @@ func TestConsumableSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/datablocks/usersync_test.go b/adapters/datablocks/usersync_test.go index f8500ab9b03..a7518e9b226 100644 --- a/adapters/datablocks/usersync_test.go +++ b/adapters/datablocks/usersync_test.go @@ -23,7 +23,7 @@ func TestDatablocksSyncer(t *testing.T) { Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/emx_digital/usersync_test.go b/adapters/emx_digital/usersync_test.go index 0e76936cea4..59d66d87808 100644 --- a/adapters/emx_digital/usersync_test.go +++ b/adapters/emx_digital/usersync_test.go @@ -23,7 +23,7 @@ func TestEMXDigitalSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/engagebdr/usersync_test.go b/adapters/engagebdr/usersync_test.go index 45e1e41e196..3a6c179addf 100644 --- a/adapters/engagebdr/usersync_test.go +++ b/adapters/engagebdr/usersync_test.go @@ -23,7 +23,7 @@ func TestEngageBDRSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/gamoshi/usersync_test.go b/adapters/gamoshi/usersync_test.go index b8e3e327e44..43dc88a4953 100644 --- a/adapters/gamoshi/usersync_test.go +++ b/adapters/gamoshi/usersync_test.go @@ -18,7 +18,7 @@ func TestGamoshiSyncer(t *testing.T) { syncer := NewGamoshiSyncer(syncURLTemplate) syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ CCPA: ccpa.Policy{ - Value: "anyValue", + Consent: "anyValue", }, }) diff --git a/adapters/gumgum/usersync_test.go b/adapters/gumgum/usersync_test.go index 3606f6ae04c..9c6dc420600 100644 --- a/adapters/gumgum/usersync_test.go +++ b/adapters/gumgum/usersync_test.go @@ -23,7 +23,7 @@ func TestGumGumSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/improvedigital/usersync_test.go b/adapters/improvedigital/usersync_test.go index c928ebf123d..35ea89cf894 100644 --- a/adapters/improvedigital/usersync_test.go +++ b/adapters/improvedigital/usersync_test.go @@ -23,7 +23,7 @@ func TestImprovedigitalSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/marsmedia/usersync_test.go b/adapters/marsmedia/usersync_test.go index 67276a35fb6..f019c014516 100644 --- a/adapters/marsmedia/usersync_test.go +++ b/adapters/marsmedia/usersync_test.go @@ -23,7 +23,7 @@ func TestMarsmediaSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go index ec9787bc20d..fa78664928f 100644 --- a/adapters/nanointeractive/usersync_test.go +++ b/adapters/nanointeractive/usersync_test.go @@ -1,11 +1,12 @@ package nanointeractive import ( - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" "testing" "text/template" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/prebid/prebid-server/privacy" "github.com/stretchr/testify/assert" ) @@ -17,16 +18,15 @@ func TestNewNanoInteractiveSyncer(t *testing.T) { ) userSync := NewNanoInteractiveSyncer(syncURLTemplate) - syncInfo, err := userSync.GetUsersyncInfo( - privacy.Policies{ - GDPR: gdpr.Policy{ - Signal: "1", - Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", - }, - CCPA: ccpa.Policy{ - Value: "1NYN", - }, - }) + syncInfo, err := userSync.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + }, + CCPA: ccpa.Policy{ + Consent: "1NYN", + }, + }) assert.NoError(t, err) assert.Equal(t, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr=1&consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24UID", syncInfo.URL) diff --git a/adapters/pubmatic/usersync_test.go b/adapters/pubmatic/usersync_test.go index dd4a086c453..d6cd9f78af7 100644 --- a/adapters/pubmatic/usersync_test.go +++ b/adapters/pubmatic/usersync_test.go @@ -23,7 +23,7 @@ func TestPubmaticSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/rhythmone/usersync_test.go b/adapters/rhythmone/usersync_test.go index cee6e9b0259..85ecba2a8ab 100644 --- a/adapters/rhythmone/usersync_test.go +++ b/adapters/rhythmone/usersync_test.go @@ -23,7 +23,7 @@ func TestRhythmoneSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 522bbc4967e..36af79c4534 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -3,16 +3,17 @@ package sharethrough import ( "encoding/json" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy/ccpa" "net/http" "net/url" "regexp" "strconv" "time" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/privacy/ccpa" ) const defaultTmax = 10000 // 10 sec @@ -97,8 +98,8 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr } usPolicySignal := "" - if usPolicy, err := ccpa.ReadPolicy(request); err == nil { - usPolicySignal = usPolicy.Value + if usPolicy, err := ccpa.ReadFromRequest(request); err == nil { + usPolicySignal = usPolicy.Consent } return &adapters.RequestData{ diff --git a/adapters/smartadserver/usersync_test.go b/adapters/smartadserver/usersync_test.go index e279b49e017..c4e6660693f 100644 --- a/adapters/smartadserver/usersync_test.go +++ b/adapters/smartadserver/usersync_test.go @@ -23,7 +23,7 @@ func TestSmartadserverSyncer(t *testing.T) { Consent: "COyASAoOyASAoAfAAAENAfCAAAAAAAAAAAAAAAAAAAAA", }, CCPA: ccpa.Policy{ - Value: "1YNN", + Consent: "1YNN", }, }) diff --git a/adapters/syncer.go b/adapters/syncer.go index c212a4366c9..122bcc7ed38 100644 --- a/adapters/syncer.go +++ b/adapters/syncer.go @@ -46,7 +46,7 @@ func (s *Syncer) GetUsersyncInfo(privacyPolicies privacy.Policies) (*usersync.Us syncURL, err := macros.ResolveMacros(*s.urlTemplate, macros.UserSyncTemplateParams{ GDPR: privacyPolicies.GDPR.Signal, GDPRConsent: privacyPolicies.GDPR.Consent, - USPrivacy: privacyPolicies.CCPA.Value, + USPrivacy: privacyPolicies.CCPA.Consent, }) if err != nil { return nil, err diff --git a/adapters/syncer_test.go b/adapters/syncer_test.go index 9be523091dd..ca33a9a130d 100644 --- a/adapters/syncer_test.go +++ b/adapters/syncer_test.go @@ -17,7 +17,7 @@ func TestGetUsersyncInfo(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, } diff --git a/adapters/unruly/usersync_test.go b/adapters/unruly/usersync_test.go index bdab254f370..2f0e07d813a 100644 --- a/adapters/unruly/usersync_test.go +++ b/adapters/unruly/usersync_test.go @@ -23,7 +23,7 @@ func TestUnrulySyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/valueimpression/usersync_test.go b/adapters/valueimpression/usersync_test.go index 63f123055a9..ffb3f372bd7 100644 --- a/adapters/valueimpression/usersync_test.go +++ b/adapters/valueimpression/usersync_test.go @@ -23,7 +23,7 @@ func TestValueImpressionSyncer(t *testing.T) { Consent: "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/adapters/visx/usersync_test.go b/adapters/visx/usersync_test.go index a77136c9240..b410cda6061 100644 --- a/adapters/visx/usersync_test.go +++ b/adapters/visx/usersync_test.go @@ -23,7 +23,7 @@ func TestVisxSyncer(t *testing.T) { Consent: "B", }, CCPA: ccpa.Policy{ - Value: "C", + Consent: "C", }, }) diff --git a/adapters/zeroclickfraud/usersync_test.go b/adapters/zeroclickfraud/usersync_test.go index 30ade771a4c..5e8f8fdf111 100644 --- a/adapters/zeroclickfraud/usersync_test.go +++ b/adapters/zeroclickfraud/usersync_test.go @@ -23,7 +23,7 @@ func TestZeroClickFraudSyncer(t *testing.T) { Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", }, CCPA: ccpa.Policy{ - Value: "1NYN", + Consent: "1NYN", }, }) diff --git a/endpoints/auction.go b/endpoints/auction.go index bf592e43b02..c6fd57123c7 100644 --- a/endpoints/auction.go +++ b/endpoints/auction.go @@ -24,7 +24,7 @@ import ( "github.com/prebid/prebid-server/pbsmetrics" pbc "github.com/prebid/prebid-server/prebid_cache_client" "github.com/prebid/prebid-server/privacy" - gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr" + gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" "github.com/prebid/prebid-server/usersync" ) @@ -190,7 +190,7 @@ func (a *auction) recoverSafely(inner func(*pbs.PBSBidder, pbsmetrics.AdapterLab } } -func (a *auction) shouldUsersync(ctx context.Context, bidder openrtb_ext.BidderName, gdprPrivacyPolicy gdprPolicy.Policy) bool { +func (a *auction) shouldUsersync(ctx context.Context, bidder openrtb_ext.BidderName, gdprPrivacyPolicy gdprPrivacy.Policy) bool { switch gdprPrivacyPolicy.Signal { case "0": return true @@ -511,7 +511,7 @@ func (a *auction) processUserSync(req *pbs.PBSRequest, bidder *pbs.PBSBidder, bl if uid == "" { bidder.NoCookie = true privacyPolicies := privacy.Policies{ - GDPR: gdprPolicy.Policy{ + GDPR: gdprPrivacy.Policy{ Signal: req.ParseGDPR(), Consent: req.ParseConsent(), }, diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go index 028f119640a..1e41b02aaa2 100644 --- a/endpoints/auction_test.go +++ b/endpoints/auction_test.go @@ -387,11 +387,12 @@ func TestShouldUsersync(t *testing.T) { }, metricsEngine: nil, } - privacyPolicy := gdprPolicy.Policy{ + gdprPrivacyPolicy := gdprPolicy.Policy{ Signal: gdprApplies, Consent: consent, } - allowSyncs := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, privacyPolicy) + + allowSyncs := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, gdprPrivacyPolicy) if allowSyncs != expectAllow { t.Errorf("Expected syncs: %t, allowed syncs: %t", expectAllow, allowSyncs) } diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 9787a8f78f2..60da4f0bd16 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -20,7 +20,7 @@ import ( "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/privacy" "github.com/prebid/prebid-server/privacy/ccpa" - gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr" + gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" "github.com/prebid/prebid-server/usersync" ) @@ -105,24 +105,30 @@ func (deps *cookieSyncDeps) Endpoint(w http.ResponseWriter, r *http.Request, _ h } } + parsedReq.filterExistingSyncs(deps.syncers, userSyncCookie, needSyncupForSameSite) + + adapterSyncs := make(map[openrtb_ext.BidderName]bool) + // assume all bidders will be privacy blocked + for _, b := range parsedReq.Bidders { + adapterSyncs[openrtb_ext.BidderName(b)] = true + } + privacyPolicy := privacy.Policies{ - GDPR: gdprPolicy.Policy{ + GDPR: gdprPrivacy.Policy{ Signal: gdprToString(parsedReq.GDPR), Consent: parsedReq.Consent, }, CCPA: ccpa.Policy{ - Value: parsedReq.USPrivacy, + Consent: parsedReq.USPrivacy, }, } - parsedReq.filterExistingSyncs(deps.syncers, userSyncCookie, needSyncupForSameSite) + parsedReq.filterForGDPR(deps.syncPermissions) - adapterSyncs := make(map[openrtb_ext.BidderName]bool) - // assume all bidders will be privacy blocked - for _, b := range parsedReq.Bidders { - adapterSyncs[openrtb_ext.BidderName(b)] = true + if deps.enforceCCPA { + parsedReq.filterForCCPA() } - parsedReq.filterForPrivacy(deps.syncPermissions, privacyPolicy, deps.enforceCCPA) + // surviving bidders are not privacy blocked for _, b := range parsedReq.Bidders { adapterSyncs[openrtb_ext.BidderName(b)] = false @@ -223,12 +229,7 @@ func (req *cookieSyncRequest) filterExistingSyncs(valid map[openrtb_ext.BidderNa } } -func (req *cookieSyncRequest) filterForPrivacy(permissions gdpr.Permissions, privacyPolicies privacy.Policies, enforceCCPA bool) { - if enforceCCPA && privacyPolicies.CCPA.ShouldEnforce() { - req.Bidders = nil - return - } - +func (req *cookieSyncRequest) filterForGDPR(permissions gdpr.Permissions) { if req.GDPR != nil && *req.GDPR == 0 { return } @@ -246,6 +247,25 @@ func (req *cookieSyncRequest) filterForPrivacy(permissions gdpr.Permissions, pri } } +func (req *cookieSyncRequest) filterForCCPA() { + validBidders := make(map[string]struct{}) + for _, v := range openrtb_ext.BidderMap { + validBidders[v.String()] = struct{}{} + } + + ccpaPolicy := &ccpa.Policy{Consent: req.USPrivacy} + ccpaParsedPolicy, err := ccpaPolicy.Parse(validBidders) + + if err == nil { + for i := 0; i < len(req.Bidders); i++ { + if ccpaParsedPolicy.ShouldEnforce(req.Bidders[i]) { + req.Bidders = append(req.Bidders[:i], req.Bidders[i+1:]...) + i-- + } + } + } +} + // filterToLimit will enforce a max limit on cookiesyncs supplied, picking a random subset of syncs to get to the limit if over. func (req *cookieSyncRequest) filterToLimit() { if req.Limit <= 0 { diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 1e92569e260..d7442f5ecba 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -24,6 +24,8 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" "github.com/prebid/prebid-server/stored_requests" "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" "github.com/prebid/prebid-server/usersync" @@ -403,17 +405,12 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope req.Imp[0].TagID = slot } - consent := readConsent(httpRequest.URL) - if consent != "" { - if policies, ok := privacy.ReadPoliciesFromConsent(consent); ok { - if err := policies.Write(req); err != nil { - return []error{err} - } - } else { - return []error{&errortypes.InvalidPrivacyConsent{ - Message: fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent), - }} - } + policyWriter, policyWriterErr := readPolicyFromUrl(httpRequest.URL) + if policyWriterErr != nil { + return []error{policyWriterErr} + } + if err := policyWriter.Write(req); err != nil { + return []error{err} } if timeout, err := strconv.ParseInt(httpRequest.FormValue("timeout"), 10, 64); err == nil { @@ -558,7 +555,27 @@ func setAmpExt(site *openrtb.Site, value string) { } } -func readConsent(url *url.URL) string { +func readPolicyFromUrl(url *url.URL) (privacy.PolicyWriter, error) { + consent := readConsentFromURL(url) + + if len(consent) == 0 { + return privacy.NilPolicyWriter{}, nil + } + + if gdpr.ValidateConsent(consent) { + return gdpr.ConsentWriter{consent}, nil + } + + if ccpa.ValidateConsent(consent) { + return ccpa.ConsentWriter{consent}, nil + } + + return privacy.NilPolicyWriter{}, &errortypes.InvalidPrivacyConsent{ + Message: fmt.Sprintf("Consent '%s' is not recognized as either CCPA or GDPR TCF.", consent), + } +} + +func readConsentFromURL(url *url.URL) string { if v := url.Query().Get("consent_string"); v != "" { return v } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index d6cbc2285fb..b02b57861bd 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -318,39 +318,36 @@ func (deps *endpointDeps) validateRequest(req *openrtb.BidRequest) []error { } if (req.Site == nil && req.App == nil) || (req.Site != nil && req.App != nil) { - errL = append(errL, errors.New("request.site or request.app must be defined, but not both.")) - return errL + return append(errL, errors.New("request.site or request.app must be defined, but not both.")) } if err := deps.validateSite(req.Site); err != nil { - errL = append(errL, err) - return errL + return append(errL, err) } if err := deps.validateApp(req.App); err != nil { - errL = append(errL, err) - return errL + return append(errL, err) } if err := validateUser(req.User, aliases); err != nil { - errL = append(errL, err) - return errL + return append(errL, err) } if err := validateRegs(req.Regs); err != nil { - errL = append(errL, err) - return errL + return append(errL, err) } - if policy, err := ccpa.ReadPolicy(req); err != nil { - errL = append(errL, errL...) - return errL - } else if err := policy.Validate(); err != nil { - errL = append(errL, &errortypes.InvalidPrivacyConsent{Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err)}) - - policy.Value = "" - if err := policy.Write(req); err != nil { - errL = append(errL, fmt.Errorf("Unable to remove invalid CCPA consent from the request. (%v)", err)) + if ccpaPolicy, err := ccpa.ReadFromRequest(req); err != nil { + return append(errL, err) + } else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(aliases)); err != nil { + if _, invalidConsent := err.(*errortypes.InvalidPrivacyConsent); invalidConsent { + errL = append(errL, &errortypes.InvalidPrivacyConsent{Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err)}) + consentWriter := ccpa.ConsentWriter{""} + if err := consentWriter.Write(req); err != nil { + return append(errL, fmt.Errorf("Unable to remove invalid CCPA consent from the request. (%v)", err)) + } + } else { + return append(errL, err) } } diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 925cffcebeb..58913bb58d6 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -1339,6 +1339,54 @@ func TestCCPAInvalid(t *testing.T) { assert.Empty(t, req.Regs.Ext, "Invalid Consent Removed From Request") } +func TestNoSaleInvalid(t *testing.T) { + deps := &endpointDeps{ + &nobidExchange{}, + newParamsValidator(t), + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, + empty_fetcher.EmptyFetcher{}, + &config.Configuration{}, + pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), + analyticsConf.NewPBSAnalytics(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BidderMap, + nil, + nil, + hardcodedResponseIPValidator{response: true}, + } + + ui := uint64(1) + req := openrtb.BidRequest{ + ID: "someID", + Imp: []openrtb.Imp{ + { + ID: "imp-ID", + Banner: &openrtb.Banner{ + W: &ui, + H: &ui, + }, + Ext: json.RawMessage(`{"appnexus": {"placementId": 5667}}`), + }, + }, + Site: &openrtb.Site{ + ID: "myID", + }, + Regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"1NYN"}`), + }, + Ext: json.RawMessage(`{"prebid":{"nosale":["*", "appnexus"]}}`), + } + + errL := deps.validateRequest(&req) + + expectedError := errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided") + assert.ElementsMatch(t, errL, []error{expectedError}) +} + func TestValidateSourceTID(t *testing.T) { cfg := &config.Configuration{ AutoGenSourceTID: true, diff --git a/exchange/exchangetest/ccpa-nosale-any-bidder.json b/exchange/exchangetest/ccpa-nosale-any-bidder.json new file mode 100644 index 00000000000..f7abd91f512 --- /dev/null +++ b/exchange/exchangetest/ccpa-nosale-any-bidder.json @@ -0,0 +1,75 @@ +{ + "enforceCcpa": true, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "us_privacy": "1-Y-" + } + }, + "ext": { + "prebid": { + "nosale": ["*"] + } + }, + "user": { + "buyeruid": "some-buyer-id" + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "us_privacy": "1-Y-" + } + }, + "ext": { + "prebid": { + "nosale": ["*"] + } + }, + "user": { + "buyeruid": "some-buyer-id" + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/ccpa-nosale-specific-bidder.json b/exchange/exchangetest/ccpa-nosale-specific-bidder.json new file mode 100644 index 00000000000..b89e29aea01 --- /dev/null +++ b/exchange/exchangetest/ccpa-nosale-specific-bidder.json @@ -0,0 +1,75 @@ +{ + "enforceCcpa": true, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "us_privacy": "1-Y-" + } + }, + "ext": { + "prebid": { + "nosale": ["appnexus"] + } + }, + "user": { + "buyeruid": "some-buyer-id" + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "us_privacy": "1-Y-" + } + }, + "ext": { + "prebid": { + "nosale": ["appnexus"] + } + }, + "user": { + "buyeruid": "some-buyer-id" + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/utils.go b/exchange/utils.go index 22b28adcacb..1e49b7acc6a 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -19,6 +19,8 @@ import ( "github.com/prebid/prebid-server/privacy/lmt" ) +const unknownBidder string = "" + func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext.ExtRequestPrebidSChainSChain, error) { bidderToSChains := make(map[string]*openrtb_ext.ExtRequestPrebidSChainSChain) @@ -65,31 +67,32 @@ func cleanOpenRTBRequests(ctx context.Context, requestsByBidder, errs = splitBidRequest(orig, requestExt, impsByBidder, aliases, usersyncs, blables, labels) + if len(requestsByBidder) == 0 { + return + } + gdpr := extractGDPR(orig, usersyncIfAmbiguous) consent := extractConsent(orig) ampGDPRException := (labels.RType == pbsmetrics.ReqTypeAMP) && gDPR.AMPException() - var ccpaPolicy ccpa.Policy - if privacyConfig.CCPA.Enforce { - ccpaPolicy, _ = ccpa.ReadPolicy(orig) + ccpaEnforcer, err := extractCCPA(orig, privacyConfig, aliases) + if err != nil { + errs = append(errs, err) + return } - var lmtPolicy lmt.Policy - if privacyConfig.LMT.Enforce { - lmtPolicy = lmt.ReadPolicy(orig) - } + lmtEnforcer := extractLMT(orig, privacyConfig) // request level privacy policies privacyEnforcement := privacy.Enforcement{ - CCPA: ccpaPolicy.ShouldEnforce(), COPPA: orig.Regs != nil && orig.Regs.COPPA == 1, - LMT: lmtPolicy.ShouldEnforce(), + LMT: lmtEnforcer.ShouldEnforce(unknownBidder), } - privacyLabels.CCPAProvided = ccpaPolicy.Value != "" - privacyLabels.CCPAEnforced = privacyEnforcement.CCPA + privacyLabels.CCPAProvided = ccpaEnforcer.CanEnforce() + privacyLabels.CCPAEnforced = ccpaEnforcer.ShouldEnforce(unknownBidder) privacyLabels.COPPAEnforced = privacyEnforcement.COPPA - privacyLabels.LMTEnforced = privacyEnforcement.LMT + privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder) if gdpr == 1 { privacyLabels.GDPREnforced = true @@ -102,7 +105,10 @@ func cleanOpenRTBRequests(ctx context.Context, // bidder level privacy policies for bidder, bidReq := range requestsByBidder { + // CCPA + privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidder.String()) + // GDPR if gdpr == 1 { coreBidder := resolveBidder(bidder.String(), aliases) @@ -121,6 +127,32 @@ func cleanOpenRTBRequests(ctx context.Context, return } +func extractCCPA(orig *openrtb.BidRequest, privacyConfig config.Privacy, aliases map[string]string) (privacy.PolicyEnforcer, error) { + ccpaPolicy, err := ccpa.ReadFromRequest(orig) + if err != nil { + return privacy.NilPolicyEnforcer{}, err + } + + validBidders := GetValidBidders(aliases) + ccpaParsedPolicy, err := ccpaPolicy.Parse(validBidders) + if err != nil { + return privacy.NilPolicyEnforcer{}, err + } + + ccpaEnforcer := privacy.EnabledPolicyEnforcer{ + Enabled: privacyConfig.CCPA.Enforce, + PolicyEnforcer: ccpaParsedPolicy, + } + return ccpaEnforcer, nil +} + +func extractLMT(orig *openrtb.BidRequest, privacyConfig config.Privacy) privacy.PolicyEnforcer { + return privacy.EnabledPolicyEnforcer{ + Enabled: privacyConfig.LMT.Enforce, + PolicyEnforcer: lmt.ReadFromRequest(orig), + } +} + func splitBidRequest(req *openrtb.BidRequest, requestExt *openrtb_ext.ExtRequest, impsByBidder map[string][]openrtb.Imp, @@ -429,6 +461,20 @@ func parseAliases(orig *openrtb.BidRequest) (map[string]string, []error) { return aliases, nil } +func GetValidBidders(aliases map[string]string) map[string]struct{} { + validBidders := make(map[string]struct{}) + + for _, v := range openrtb_ext.BidderMap { + validBidders[v.String()] = struct{}{} + } + + for k := range aliases { + validBidders[k] = struct{}{} + } + + return validBidders +} + // Quick little randomizer for a list of strings. Stuffing it in utils to keep other files clean func randomizeList(list []openrtb_ext.BidderName) { l := len(list) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 528e875ab16..0dd6c0311ab 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -3,11 +3,13 @@ package exchange import ( "context" "encoding/json" + "errors" "fmt" "testing" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbsmetrics" "github.com/stretchr/testify/assert" @@ -93,6 +95,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { func TestCleanOpenRTBRequestsCCPA(t *testing.T) { testCases := []struct { description string + reqExt json.RawMessage ccpaConsent string enforceCCPA bool expectDataScrub bool @@ -118,13 +121,46 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { CCPAEnforced: false, }, }, + { + description: "Feature Flag Enabled - No Sale Star - Doesn't Scrub", + reqExt: json.RawMessage(`{"prebid":{"nosale":["*"]}}`), + ccpaConsent: "1-Y-", + enforceCCPA: true, + expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: true, + CCPAEnforced: false, + }, + }, + { + description: "Feature Flag Enabled - No Sale Specific Bidder - Doesn't Scrub", + reqExt: json.RawMessage(`{"prebid":{"nosale":["appnexus"]}}`), + ccpaConsent: "1-Y-", + enforceCCPA: true, + expectDataScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: true, + CCPAEnforced: true, + }, + }, + { + description: "Feature Flag Enabled - No Sale Different Bidder - Scrubs", + reqExt: json.RawMessage(`{"prebid":{"nosale":["rubicon"]}}`), + ccpaConsent: "1-Y-", + enforceCCPA: true, + expectDataScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + CCPAProvided: true, + CCPAEnforced: true, + }, + }, { description: "Feature Flag Disabled", ccpaConsent: "1-Y-", enforceCCPA: false, expectDataScrub: false, expectPrivacyLabels: pbsmetrics.PrivacyLabels{ - CCPAProvided: false, + CCPAProvided: true, CCPAEnforced: false, }, }, @@ -132,6 +168,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { for _, test := range testCases { req := newBidRequest(t) + req.Ext = test.reqExt req.Regs = &openrtb.Regs{ Ext: json.RawMessage(`{"us_privacy":"` + test.ccpaConsent + `"}`), } @@ -157,6 +194,47 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { } } +func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { + testCases := []struct { + description string + reqExt json.RawMessage + reqRegsExt json.RawMessage + expectError error + }{ + { + description: "Invalid Consent", + reqExt: json.RawMessage(`{"prebid":{"nosale":["*"]}}`), + reqRegsExt: json.RawMessage(`{"us_privacy":"malformed"}`), + expectError: &errortypes.InvalidPrivacyConsent{"request.regs.ext.us_privacy must contain 4 characters"}, + }, + { + description: "Invalid No Sale Bidders", + reqExt: json.RawMessage(`{"prebid":{"nosale":["*", "another"]}}`), + reqRegsExt: json.RawMessage(`{"us_privacy":"1NYN"}`), + expectError: errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided"), + }, + } + + for _, test := range testCases { + req := newBidRequest(t) + req.Ext = test.reqExt + req.Regs = &openrtb.Regs{Ext: test.reqRegsExt} + + var reqExtStruct openrtb_ext.ExtRequest + err := json.Unmarshal(req.Ext, &reqExtStruct) + assert.NoError(t, err, test.description+":marshal_ext") + + privacyConfig := config.Privacy{ + CCPA: config.CCPA{ + Enforce: true, + }, + } + _, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &reqExtStruct, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + + assert.ElementsMatch(t, []error{test.expectError}, errs, test.description) + } +} + func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { testCases := []struct { description string diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 42ac9d9d4b9..894be6763c6 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -24,6 +24,11 @@ type ExtRequestPrebid struct { Targeting *ExtRequestTargeting `json:"targeting,omitempty"` SupportDeals bool `json:"supportdeals,omitempty"` Debug bool `json:"debug,omitempty"` + + // NoSale specifies bidders with whom the publisher has a legal relationship where the + // passing of personally identifiable information doesn't constitute a sale per CCPA law. + // The array may contain a single sstar ('*') entry to represent all bidders. + NoSale []string `json:"nosale,omitempty"` } // ExtRequestPrebid defines the contract for bidrequest.ext.prebid.schains diff --git a/privacy/ccpa/consentwriter.go b/privacy/ccpa/consentwriter.go new file mode 100644 index 00000000000..4856655402b --- /dev/null +++ b/privacy/ccpa/consentwriter.go @@ -0,0 +1,25 @@ +package ccpa + +import ( + "github.com/mxmCherry/openrtb" +) + +// ConsentWriter implements the PolicyWriter interface for CCPA. +type ConsentWriter struct { + Consent string +} + +// Write mutates an OpenRTB bid request with the CCPA consent string. +func (c ConsentWriter) Write(req *openrtb.BidRequest) error { + if req == nil { + return nil + } + + regs, err := buildRegs(c.Consent, req.Regs) + if err != nil { + return err + } + req.Regs = regs + + return nil +} diff --git a/privacy/ccpa/consentwriter_test.go b/privacy/ccpa/consentwriter_test.go new file mode 100644 index 00000000000..57a7f8f4ddf --- /dev/null +++ b/privacy/ccpa/consentwriter_test.go @@ -0,0 +1,51 @@ +package ccpa + +import ( + "encoding/json" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/stretchr/testify/assert" +) + +func TestConsentWriter(t *testing.T) { + consent := "anyConsent" + testCases := []struct { + description string + request *openrtb.BidRequest + expected *openrtb.BidRequest + expectedError bool + }{ + { + description: "Nil Request", + request: nil, + expected: nil, + }, + { + description: "Success", + request: &openrtb.BidRequest{}, + expected: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, + }, + }, + { + description: "Error With Regs.Ext - Does Not Mutate", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`malformed}`)}, + }, + expectedError: true, + expected: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`malformed}`)}, + }, + }, + } + + for _, test := range testCases { + writer := ConsentWriter{consent} + + err := writer.Write(test.request) + + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, test.request, test.description) + } +} diff --git a/privacy/ccpa/parsedpolicy.go b/privacy/ccpa/parsedpolicy.go new file mode 100644 index 00000000000..3c934e67822 --- /dev/null +++ b/privacy/ccpa/parsedpolicy.go @@ -0,0 +1,137 @@ +package ccpa + +import ( + "errors" + "fmt" + + "github.com/prebid/prebid-server/errortypes" +) + +const ( + ccpaVersion1 = '1' + ccpaYes = 'Y' + ccpaNo = 'N' + ccpaNotApplicable = '-' +) + +const ( + indexVersion = 0 + indexExplicitNotice = 1 + indexOptOutSale = 2 + indexLSPACoveredTransaction = 3 +) + +const allBiddersMarker = "*" + +// ValidateConsent returns true if the consent string is empty or valid per the IAB CCPA spec. +func ValidateConsent(consent string) bool { + _, err := parseConsent(consent) + return err == nil +} + +// ParsedPolicy represents parsed and validated CCPA regulatory information. Use this struct +// to make enforcement decisions. +type ParsedPolicy struct { + consentSpecified bool + consentOptOutSale bool + noSaleForAllBidders bool + noSaleSpecificBidders map[string]struct{} +} + +// Parse returns a parsed and validated ParsedPolicy intended for use in enforcement decisions. +func (p Policy) Parse(validBidders map[string]struct{}) (ParsedPolicy, error) { + consentOptOut, err := parseConsent(p.Consent) + if err != nil { + msg := fmt.Sprintf("request.regs.ext.us_privacy %s", err.Error()) + return ParsedPolicy{}, &errortypes.InvalidPrivacyConsent{Message: msg} + } + + noSaleForAllBidders, noSaleSpecificBidders, err := parseNoSaleBidders(p.NoSaleBidders, validBidders) + if err != nil { + return ParsedPolicy{}, fmt.Errorf("request.ext.prebid.nosale is invalid: %s", err.Error()) + } + + return ParsedPolicy{ + consentSpecified: p.Consent != "", + consentOptOutSale: consentOptOut, + noSaleForAllBidders: noSaleForAllBidders, + noSaleSpecificBidders: noSaleSpecificBidders, + }, nil +} + +func parseConsent(consent string) (consentOptOutSale bool, err error) { + if consent == "" { + return false, nil + } + + if len(consent) != 4 { + return false, errors.New("must contain 4 characters") + } + + if consent[indexVersion] != ccpaVersion1 { + return false, errors.New("must specify version 1") + } + + var c byte + + c = consent[indexExplicitNotice] + if c != ccpaNo && c != ccpaYes && c != ccpaNotApplicable { + return false, errors.New("must specify 'N', 'Y', or '-' for the explicit notice") + } + + c = consent[indexOptOutSale] + if c != ccpaNo && c != ccpaYes && c != ccpaNotApplicable { + return false, errors.New("must specify 'N', 'Y', or '-' for the opt-out sale") + } + + c = consent[indexLSPACoveredTransaction] + if c != ccpaNo && c != ccpaYes && c != ccpaNotApplicable { + return false, errors.New("must specify 'N', 'Y', or '-' for the limited service provider agreement") + } + + return consent[indexOptOutSale] == ccpaYes, nil +} + +func parseNoSaleBidders(noSaleBidders []string, validBidders map[string]struct{}) (noSaleForAllBidders bool, noSaleSpecificBidders map[string]struct{}, err error) { + noSaleSpecificBidders = make(map[string]struct{}) + + if len(noSaleBidders) == 1 && noSaleBidders[0] == allBiddersMarker { + noSaleForAllBidders = true + return + } + + for _, bidder := range noSaleBidders { + if bidder == allBiddersMarker { + err = errors.New("can only specify all bidders if no other bidders are provided") + return + } + + if _, exists := validBidders[bidder]; exists { + noSaleSpecificBidders[bidder] = struct{}{} + } else { + err = fmt.Errorf("unrecognized bidder '%s'", bidder) + return + } + } + + return +} + +// CanEnforce returns true when consent is specifically provided by the publisher, as opposed to an empty string. +func (p ParsedPolicy) CanEnforce() bool { + return p.consentSpecified +} + +func (p ParsedPolicy) isNoSaleForBidder(bidder string) bool { + if p.noSaleForAllBidders { + return true + } + + _, exists := p.noSaleSpecificBidders[bidder] + return exists +} + +// ShouldEnforce returns true when the opt-out signal is explicitly detected. +func (p ParsedPolicy) ShouldEnforce(bidder string) bool { + return !p.isNoSaleForBidder(bidder) && p.consentOptOutSale +} diff --git a/privacy/ccpa/parsedpolicy_test.go b/privacy/ccpa/parsedpolicy_test.go new file mode 100644 index 00000000000..2f7e8bfd683 --- /dev/null +++ b/privacy/ccpa/parsedpolicy_test.go @@ -0,0 +1,391 @@ +package ccpa + +import ( + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestValidateConsent(t *testing.T) { + testCases := []struct { + description string + consent string + expected bool + }{ + { + description: "Empty String", + consent: "", + expected: true, + }, + { + description: "Valid Consent With Opt Out", + consent: "1NYN", + expected: true, + }, + { + description: "Valid Consent Without Opt Out", + consent: "1NNN", + expected: true, + }, + { + description: "Invalid", + consent: "malformed", + expected: false, + }, + } + + for _, test := range testCases { + result := ValidateConsent(test.consent) + assert.Equal(t, test.expected, result, test.description) + } +} + +func TestParse(t *testing.T) { + validBidders := map[string]struct{}{"a": {}} + + testCases := []struct { + description string + consent string + noSaleBidders []string + expectedPolicy ParsedPolicy + expectedError string + }{ + { + description: "Consent Error", + consent: "malformed", + noSaleBidders: []string{}, + expectedPolicy: ParsedPolicy{}, + expectedError: "request.regs.ext.us_privacy must contain 4 characters", + }, + { + description: "No Sale Error", + consent: "1NYN", + noSaleBidders: []string{"b"}, + expectedPolicy: ParsedPolicy{}, + expectedError: "request.ext.prebid.nosale is invalid: unrecognized bidder 'b'", + }, + { + description: "Success", + consent: "1NYN", + noSaleBidders: []string{"a"}, + expectedPolicy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: true, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{"a": {}}, + }, + }, + } + + for _, test := range testCases { + policy := Policy{test.consent, test.noSaleBidders} + + result, err := policy.Parse(validBidders) + + if test.expectedError == "" { + assert.NoError(t, err, test.description) + } else { + assert.EqualError(t, err, test.expectedError, test.description) + } + + assert.Equal(t, test.expectedPolicy, result, test.description) + } +} + +func TestParseConsent(t *testing.T) { + testCases := []struct { + description string + consent string + expectedResult bool + expectedError string + }{ + { + description: "Valid", + consent: "1NYN", + expectedResult: true, + }, + { + description: "Valid - Not Sale", + consent: "1NNN", + expectedResult: false, + }, + { + description: "Valid - Not Applicable", + consent: "1---", + expectedResult: false, + }, + { + description: "Valid - Empty", + consent: "", + expectedResult: false, + }, + { + description: "Wrong Length", + consent: "1NY", + expectedResult: false, + expectedError: "must contain 4 characters", + }, + { + description: "Wrong Version", + consent: "2---", + expectedResult: false, + expectedError: "must specify version 1", + }, + { + description: "Explicit Notice Char", + consent: "1X--", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + }, + { + description: "Invalid Explicit Notice Case", + consent: "1y--", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + }, + { + description: "Invalid Opt-Out Sale Char", + consent: "1-X-", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + }, + { + description: "Invalid Opt-Out Sale Case", + consent: "1-y-", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + }, + { + description: "Invalid LSPA Char", + consent: "1--X", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + }, + { + description: "Invalid LSPA Case", + consent: "1--y", + expectedResult: false, + expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + }, + } + + for _, test := range testCases { + result, err := parseConsent(test.consent) + + if test.expectedError == "" { + assert.NoError(t, err, test.description) + } else { + assert.EqualError(t, err, test.expectedError, test.description) + } + + assert.Equal(t, test.expectedResult, result, test.description) + } +} + +func TestParseNoSaleBidders(t *testing.T) { + testCases := []struct { + description string + noSaleBidders []string + validBidders []string + expectedNoSaleForAllBidders bool + expectedNoSaleSpecificBidders map[string]struct{} + expectedError string + }{ + { + description: "Valid - No Bidders", + noSaleBidders: []string{}, + validBidders: []string{"a"}, + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{}, + }, + { + description: "Valid - 1 Bidder", + noSaleBidders: []string{"a"}, + validBidders: []string{"a"}, + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{"a": {}}, + }, + { + description: "Valid - 1+ Bidders", + noSaleBidders: []string{"a", "b"}, + validBidders: []string{"a", "b"}, + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{"a": {}, "b": {}}, + }, + { + description: "Valid - All Bidders", + noSaleBidders: []string{"*"}, + validBidders: []string{"a"}, + expectedNoSaleForAllBidders: true, + expectedNoSaleSpecificBidders: map[string]struct{}{}, + }, + { + description: "Bidder Not Valid", + noSaleBidders: []string{"b"}, + validBidders: []string{"a"}, + expectedError: "unrecognized bidder 'b'", + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{}, + }, + { + description: "All Bidder Mixed With Other Bidders Is Invalid", + noSaleBidders: []string{"*", "a"}, + validBidders: []string{"a"}, + expectedError: "can only specify all bidders if no other bidders are provided", + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{}, + }, + { + description: "Valid Bidders Case Sensitive", + noSaleBidders: []string{"a"}, + validBidders: []string{"A"}, + expectedError: "unrecognized bidder 'a'", + expectedNoSaleForAllBidders: false, + expectedNoSaleSpecificBidders: map[string]struct{}{}, + }, + } + + for _, test := range testCases { + validBiddersMap := make(map[string]struct{}) + for _, v := range test.validBidders { + validBiddersMap[v] = struct{}{} + } + + resultNoSaleForAllBidders, resultNoSaleSpecificBidders, err := parseNoSaleBidders(test.noSaleBidders, validBiddersMap) + + if test.expectedError == "" { + assert.NoError(t, err, test.description+":err") + } else { + assert.EqualError(t, err, test.expectedError, test.description+":err") + } + + assert.Equal(t, test.expectedNoSaleForAllBidders, resultNoSaleForAllBidders, test.description+":allBidders") + assert.Equal(t, test.expectedNoSaleSpecificBidders, resultNoSaleSpecificBidders, test.description+":specificBidders") + } +} + +func TestCanEnforce(t *testing.T) { + testCases := []struct { + description string + policy ParsedPolicy + expected bool + }{ + { + description: "Specified", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: false, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{}, + }, + expected: true, + }, + { + description: "Not Specified", + policy: ParsedPolicy{ + consentSpecified: false, + consentOptOutSale: false, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{}, + }, + expected: false, + }, + } + + for _, test := range testCases { + result := test.policy.CanEnforce() + assert.Equal(t, test.expected, result, test.description) + } +} + +func TestShouldEnforce(t *testing.T) { + testCases := []struct { + description string + policy ParsedPolicy + bidder string + expected bool + }{ + { + description: "Not Enforced - All Bidders No Sale", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: true, + noSaleForAllBidders: true, + noSaleSpecificBidders: map[string]struct{}{}, + }, + bidder: "a", + expected: false, + }, + { + description: "Not Enforced - Specific Bidders No Sale", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: true, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{"a": {}}, + }, + bidder: "a", + expected: false, + }, + { + description: "Not Enforced - No Bidder No Sale", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: false, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{}, + }, + bidder: "a", + expected: false, + }, + { + description: "Not Enforced - No Sale Case Sensitive", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: false, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{"A": {}}, + }, + bidder: "a", + expected: false, + }, + { + description: "Enforced - No Bidder No Sale", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: true, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{}, + }, + bidder: "a", + expected: true, + }, + { + description: "Enforced - No Sale Case Sensitive", + policy: ParsedPolicy{ + consentSpecified: true, + consentOptOutSale: true, + noSaleForAllBidders: false, + noSaleSpecificBidders: map[string]struct{}{"A": {}}, + }, + bidder: "a", + expected: true, + }, + } + + for _, test := range testCases { + result := test.policy.ShouldEnforce(test.bidder) + assert.Equal(t, test.expected, result, test.description) + } +} + +type mockPolicWriter struct { + mock.Mock +} + +func (m *mockPolicWriter) Write(req *openrtb.BidRequest) error { + args := m.Called(req) + return args.Error(0) +} diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go index 11ac434595a..a9f1c49e47d 100644 --- a/privacy/ccpa/policy.go +++ b/privacy/ccpa/policy.go @@ -9,139 +9,190 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) -// Policy represents the CCPA regulation for an OpenRTB bid request. +// Policy represents the CCPA regulatory information from an OpenRTB bid request. type Policy struct { - Value string + Consent string + NoSaleBidders []string } -// ReadPolicy extracts the CCPA regulation policy from an OpenRTB request. -func ReadPolicy(req *openrtb.BidRequest) (Policy, error) { - policy := Policy{} +// ReadFromRequest extracts the CCPA regulatory information from an OpenRTB bid request. +func ReadFromRequest(req *openrtb.BidRequest) (Policy, error) { + var consent string + var noSaleBidders []string - if req != nil && req.Regs != nil && len(req.Regs.Ext) > 0 { + if req == nil { + return Policy{}, nil + } + + // Read consent from request.regs.ext + if req.Regs != nil && len(req.Regs.Ext) > 0 { var ext openrtb_ext.ExtRegs if err := json.Unmarshal(req.Regs.Ext, &ext); err != nil { - return policy, err + return Policy{}, fmt.Errorf("error reading request.regs.ext: %s", err) } - policy.Value = ext.USPrivacy + consent = ext.USPrivacy } - return policy, nil + // Read no sale bidders from request.ext.prebid + if len(req.Ext) > 0 { + var ext openrtb_ext.ExtRequest + if err := json.Unmarshal(req.Ext, &ext); err != nil { + return Policy{}, fmt.Errorf("error reading request.ext.prebid: %s", err) + } + noSaleBidders = ext.Prebid.NoSale + } + + return Policy{consent, noSaleBidders}, nil } -// Write mutates an OpenRTB bid request with the context of the CCPA policy. +// Write mutates an OpenRTB bid request with the CCPA regulatory information. func (p Policy) Write(req *openrtb.BidRequest) error { - if p.Value == "" { - return clearPolicy(req) - } - if req == nil { return nil } - if req.Regs == nil { - req.Regs = &openrtb.Regs{} + regs, err := buildRegs(p.Consent, req.Regs) + if err != nil { + return err } - - if req.Regs.Ext == nil { - ext, err := json.Marshal(openrtb_ext.ExtRegs{USPrivacy: p.Value}) - if err == nil { - req.Regs.Ext = ext - } + ext, err := buildExt(p.NoSaleBidders, req.Ext) + if err != nil { return err } - var extMap map[string]interface{} - err := json.Unmarshal(req.Regs.Ext, &extMap) - if err == nil { - extMap["us_privacy"] = p.Value - ext, err := json.Marshal(extMap) - if err == nil { - req.Regs.Ext = ext - } + req.Regs = regs + req.Ext = ext + return nil +} + +func buildRegs(consent string, regs *openrtb.Regs) (*openrtb.Regs, error) { + if consent == "" { + return buildRegsClear(regs) } - return err + return buildRegsWrite(consent, regs) } -func clearPolicy(req *openrtb.BidRequest) error { - if req == nil { - return nil +func buildRegsClear(regs *openrtb.Regs) (*openrtb.Regs, error) { + if regs == nil || len(regs.Ext) == 0 { + return regs, nil } - if req.Regs == nil { - return nil + var extMap map[string]interface{} + if err := json.Unmarshal(regs.Ext, &extMap); err != nil { + return nil, err } - if len(req.Regs.Ext) == 0 { - return nil + delete(extMap, "us_privacy") + + // Remove entire ext if it's now empty + if len(extMap) == 0 { + regsResult := *regs + regsResult.Ext = nil + return ®sResult, nil } - var extMap map[string]interface{} - err := json.Unmarshal(req.Regs.Ext, &extMap) + // Marshal ext if there are still other fields + var regsResult openrtb.Regs + ext, err := json.Marshal(extMap) if err == nil { - delete(extMap, "us_privacy") - if len(extMap) == 0 { - req.Regs.Ext = nil - } else { - ext, err := json.Marshal(extMap) - if err == nil { - req.Regs.Ext = ext - } - return err - } + regsResult = *regs + regsResult.Ext = ext } - - return err + return ®sResult, err } -// Validate returns an error if the CCPA policy does not adhere to the IAB spec. -func (p Policy) Validate() error { - if err := ValidateConsent(p.Value); err != nil { - return fmt.Errorf("request.regs.ext.us_privacy %s", err.Error()) +func buildRegsWrite(consent string, regs *openrtb.Regs) (*openrtb.Regs, error) { + if regs == nil { + return marshalRegsExt(openrtb.Regs{}, openrtb_ext.ExtRegs{USPrivacy: consent}) } - return nil + if regs.Ext == nil { + return marshalRegsExt(*regs, openrtb_ext.ExtRegs{USPrivacy: consent}) + } + + var extMap map[string]interface{} + if err := json.Unmarshal(regs.Ext, &extMap); err != nil { + return nil, err + } + + extMap["us_privacy"] = consent + return marshalRegsExt(*regs, extMap) } -// ValidateConsent returns an error if the CCPA consent string does not adhere to the IAB spec. -func ValidateConsent(consent string) error { - if consent == "" { - return nil +func marshalRegsExt(regs openrtb.Regs, ext interface{}) (*openrtb.Regs, error) { + extJSON, err := json.Marshal(ext) + if err == nil { + regs.Ext = extJSON } + return ®s, err +} - if len(consent) != 4 { - return errors.New("must contain 4 characters") +func buildExt(noSaleBidders []string, ext json.RawMessage) (json.RawMessage, error) { + if len(noSaleBidders) == 0 { + return buildExtClear(ext) } + return buildExtWrite(noSaleBidders, ext) +} - if consent[0] != '1' { - return errors.New("must specify version 1") +func buildExtClear(ext json.RawMessage) (json.RawMessage, error) { + if len(ext) == 0 { + return ext, nil } - var c byte + var extMap map[string]interface{} + if err := json.Unmarshal(ext, &extMap); err != nil { + return nil, err + } - c = consent[1] - if c != 'N' && c != 'Y' && c != '-' { - return errors.New("must specify 'N', 'Y', or '-' for the explicit notice") + prebidExt, exists := extMap["prebid"] + if !exists { + return ext, nil } - c = consent[2] - if c != 'N' && c != 'Y' && c != '-' { - return errors.New("must specify 'N', 'Y', or '-' for the opt-out sale") + // Verify prebid is an object + prebidExtMap, ok := prebidExt.(map[string]interface{}) + if !ok { + return nil, errors.New("request.ext.prebid is not a json object") } - c = consent[3] - if c != 'N' && c != 'Y' && c != '-' { - return errors.New("must specify 'N', 'Y', or '-' for the limited service provider agreement") + // Remove no sale member + delete(prebidExtMap, "nosale") + if len(prebidExtMap) == 0 { + delete(extMap, "prebid") } - return nil + // Remove entire ext if it's empty + if len(extMap) == 0 { + return nil, nil + } + + return json.Marshal(extMap) } -// ShouldEnforce returns true when the opt-out signal is explicitly detected. -func (p Policy) ShouldEnforce() bool { - if err := p.Validate(); err != nil { - return false +func buildExtWrite(noSaleBidders []string, ext json.RawMessage) (json.RawMessage, error) { + if len(ext) == 0 { + return json.Marshal(openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{NoSale: noSaleBidders}}) + } + + var extMap map[string]interface{} + if err := json.Unmarshal(ext, &extMap); err != nil { + return nil, err + } + + var prebidExt map[string]interface{} + if prebidExtInterface, exists := extMap["prebid"]; exists { + // Reference Existing Prebid Ext Map + if prebidExtMap, ok := prebidExtInterface.(map[string]interface{}); ok { + prebidExt = prebidExtMap + } else { + return nil, errors.New("request.ext.prebid is not a json object") + } + } else { + // Create New Empty Prebid Ext Map + prebidExt = make(map[string]interface{}) + extMap["prebid"] = prebidExt } - return p.Value != "" && p.Value[2] == 'Y' + prebidExt["nosale"] = noSaleBidders + return json.Marshal(extMap) } diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go index e9b4c4525b1..7ff896e9ebf 100644 --- a/privacy/ccpa/policy_test.go +++ b/privacy/ccpa/policy_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRead(t *testing.T) { +func TestReadFromRequest(t *testing.T) { testCases := []struct { description string request *openrtb.BidRequest @@ -18,83 +18,146 @@ func TestRead(t *testing.T) { { description: "Success", request: &openrtb.BidRequest{ - Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"ABC"}`), - }, + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), }, expectedPolicy: Policy{ - Value: "ABC", + Consent: "ABC", + NoSaleBidders: []string{"a", "b"}, }, }, { - description: "Empty - No Request", + description: "Nil Request", request: nil, expectedPolicy: Policy{ - Value: "", + Consent: "", + NoSaleBidders: nil, }, }, { - description: "Empty - No Regs", + description: "Nil Regs", request: &openrtb.BidRequest{ Regs: nil, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), }, expectedPolicy: Policy{ - Value: "", + Consent: "", + NoSaleBidders: []string{"a", "b"}, }, }, { - description: "Empty - No Ext", + description: "Nil Regs.Ext", request: &openrtb.BidRequest{ Regs: &openrtb.Regs{}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), }, expectedPolicy: Policy{ - Value: "", + Consent: "", + NoSaleBidders: []string{"a", "b"}, }, }, { - description: "Empty - No Value", + description: "Empty Regs.Ext", request: &openrtb.BidRequest{ - Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"anythingElse":"42"}`), - }, + Regs: &openrtb.Regs{Ext: json.RawMessage(`{}`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), }, expectedPolicy: Policy{ - Value: "", + Consent: "", + NoSaleBidders: []string{"a", "b"}, }, }, { - description: "Serialization Issue", + description: "Missing Regs.Ext USPrivacy Value", request: &openrtb.BidRequest{ - Regs: &openrtb.Regs{ - Ext: json.RawMessage(`malformed`), - }, + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"anythingElse":"42"}`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), + }, + expectedPolicy: Policy{ + Consent: "", + NoSaleBidders: []string{"a", "b"}, + }, + }, + { + description: "Malformed Regs.Ext", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`malformed`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), + }, + expectedError: true, + }, + { + description: "Invalid Regs.Ext Type", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":123`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), + }, + expectedError: true, + }, + { + description: "Nil Ext", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: nil, + }, + expectedPolicy: Policy{ + Consent: "ABC", + NoSaleBidders: nil, + }, + }, + { + description: "Empty Ext", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: json.RawMessage(`{}`), + }, + expectedPolicy: Policy{ + Consent: "ABC", + NoSaleBidders: nil, + }, + }, + { + description: "Missing Ext.Prebid No Sale Value", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: json.RawMessage(`{"anythingElse":"42"}`), + }, + expectedPolicy: Policy{ + Consent: "ABC", + NoSaleBidders: nil, + }, + }, + { + description: "Malformed Ext", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: json.RawMessage(`malformed`), + }, + expectedError: true, + }, + { + description: "Invalid Ext.Prebid.NoSale Type", + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":"wrongtype"}}`), }, expectedError: true, }, { description: "Injection Attack", request: &openrtb.BidRequest{ - Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }, + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`)}, }, expectedPolicy: Policy{ - Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"", + Consent: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"", }, }, } for _, test := range testCases { - - p, e := ReadPolicy(test.request) - - if test.expectedError { - assert.Error(t, e, test.description) - } else { - assert.NoError(t, e, test.description) - } - - assert.Equal(t, test.expectedPolicy, p, test.description) + result, err := ReadFromRequest(test.request) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expectedPolicy, result, test.description) } } @@ -107,313 +170,422 @@ func TestWrite(t *testing.T) { expectedError bool }{ { - description: "Disabled", - policy: Policy{Value: ""}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{}, - }, - { - description: "Disabled - Nil Request", - policy: Policy{Value: ""}, + description: "Nil Request", + policy: Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}}, request: nil, expected: nil, }, { - description: "Disabled - Empty Regs.Ext", - policy: Policy{Value: ""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, + description: "Success", + policy: Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}}, + request: &openrtb.BidRequest{}, + expected: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, + Ext: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), + }, }, { - description: "Disabled - Remove From Request", - policy: Policy{Value: ""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"toBeRemoved"}`)}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, + description: "Error Regs.Ext - No Partial Update To Request", + policy: Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}}, + request: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`malformed}`)}, + }, + expectedError: true, + expected: &openrtb.BidRequest{ + Regs: &openrtb.Regs{Ext: json.RawMessage(`malformed}`)}, + }, }, { - description: "Disabled - Remove From Request, Leave Other req Values", - policy: Policy{Value: ""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - COPPA: 42, - Ext: json.RawMessage(`{"us_privacy":"toBeRemoved"}`)}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - COPPA: 42}}, + description: "Error Ext - No Partial Update To Request", + policy: Policy{Consent: "anyConsent", NoSaleBidders: []string{"a", "b"}}, + request: &openrtb.BidRequest{ + Ext: json.RawMessage(`malformed}`), + }, + expectedError: true, + expected: &openrtb.BidRequest{ + Ext: json.RawMessage(`malformed}`), + }, }, + } + + for _, test := range testCases { + err := test.policy.Write(test.request) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, test.request, test.description) + } +} + +func TestBuildRegs(t *testing.T) { + testCases := []struct { + description string + consent string + regs *openrtb.Regs + expected *openrtb.Regs + expectedError bool + }{ { - description: "Disabled - Remove From Request, Leave Other req.ext Values", - policy: Policy{Value: ""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any","us_privacy":"toBeRemoved"}`)}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any"}`)}}, + description: "Clear", + consent: "", + regs: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"ABC"}`), + }, + expected: &openrtb.Regs{}, }, { - description: "Enabled - Nil Request", - policy: Policy{Value: "anyValue"}, - request: nil, - expected: nil, + description: "Clear - Error", + consent: "", + regs: &openrtb.Regs{ + Ext: json.RawMessage(`malformed`), + }, + expectedError: true, }, { - description: "Enabled With Nil Request Regs Object", - policy: Policy{Value: "anyValue"}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"anyValue"}`)}}, + description: "Write", + consent: "anyConsent", + regs: nil, + expected: &openrtb.Regs{ + Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`), + }, }, { - description: "Enabled With Nil Request Regs Ext Object", - policy: Policy{Value: "anyValue"}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"anyValue"}`)}}, + description: "Write - Error", + consent: "anyConsent", + regs: &openrtb.Regs{ + Ext: json.RawMessage(`malformed`), + }, + expectedError: true, }, + } + + for _, test := range testCases { + result, err := buildRegs(test.consent, test.regs) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, result, test.description) + } +} + +func TestBuildRegsClear(t *testing.T) { + testCases := []struct { + description string + regs *openrtb.Regs + expected *openrtb.Regs + expectedError bool + }{ { - description: "Enabled With Existing Request Regs Ext Object - Doesn't Overwrite", - policy: Policy{Value: "anyValue"}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any"}`)}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any","us_privacy":"anyValue"}`)}}, + description: "Nil Regs", + regs: nil, + expected: nil, }, { - description: "Enabled With Existing Request Regs Ext Object - Overwrites", - policy: Policy{Value: "anyValue"}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any","us_privacy":"toBeOverwritten"}`)}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any","us_privacy":"anyValue"}`)}}, + description: "Nil Regs.Ext", + regs: &openrtb.Regs{Ext: nil}, + expected: &openrtb.Regs{Ext: nil}, }, { - description: "Enabled With Existing Malformed Request Regs Ext Object", - policy: Policy{Value: "anyValue"}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`malformed`)}}, - expectedError: true, + description: "Empty Regs.Ext", + regs: &openrtb.Regs{Ext: json.RawMessage(`{}`)}, + expected: &openrtb.Regs{}, }, { - description: "Injection Attack With Nil Request Regs Object", - policy: Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }}, + description: "Removes Regs.Ext Entirely", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + expected: &openrtb.Regs{}, + }, + { + description: "Leaves Other Regs.Ext Values", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC", "other":"any"}`)}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"other":"any"}`)}, }, { - description: "Injection Attack With Nil Request Regs Ext Object", - policy: Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{}}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }}, + description: "Invalid Regs.Ext Type - Still Cleared", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)}, + expected: &openrtb.Regs{}, }, { - description: "Injection Attack With Existing Request Regs Ext Object", - policy: Policy{Value: "1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any"}`), - }}, - expected: &openrtb.BidRequest{Regs: &openrtb.Regs{ - Ext: json.RawMessage(`{"existing":"any","us_privacy":"1YYY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }}, + description: "Malformed Regs.Ext", + regs: &openrtb.Regs{Ext: json.RawMessage(`malformed`)}, + expectedError: true, }, } for _, test := range testCases { - err := test.policy.Write(test.request) - - if test.expectedError { - assert.Error(t, err, test.description) - } else { - assert.NoError(t, err, test.description) - assert.Equal(t, test.expected, test.request, test.description) - } + result, err := buildRegsClear(test.regs) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, result, test.description) } } -func TestValidate(t *testing.T) { +func TestBuildRegsWrite(t *testing.T) { testCases := []struct { description string - policy Policy - expectedError string + consent string + regs *openrtb.Regs + expected *openrtb.Regs + expectedError bool }{ { - description: "Valid", - policy: Policy{Value: "1NYN"}, - expectedError: "", + description: "Nil Regs", + consent: "anyConsent", + regs: nil, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, }, { - description: "Valid - Not Applicable", - policy: Policy{Value: "1---"}, - expectedError: "", + description: "Nil Regs.Ext", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: nil}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, }, { - description: "Valid - Empty", - policy: Policy{Value: ""}, - expectedError: "", + description: "Empty Regs.Ext", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: json.RawMessage(`{}`)}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, }, { - description: "Invalid Length", - policy: Policy{Value: "1NY"}, - expectedError: "request.regs.ext.us_privacy must contain 4 characters", + description: "Overwrites Existing", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"ABC"}`)}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, }, { - description: "Invalid Version", - policy: Policy{Value: "2---"}, - expectedError: "request.regs.ext.us_privacy must specify version 1", + description: "Leaves Other Ext Values", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"other":"any"}`)}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"other":"any","us_privacy":"anyConsent"}`)}, }, { - description: "Invalid Explicit Notice Char", - policy: Policy{Value: "1X--"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", + description: "Invalid Regs.Ext Type - Still Overwrites", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":123}`)}, + expected: &openrtb.Regs{Ext: json.RawMessage(`{"us_privacy":"anyConsent"}`)}, }, { - description: "Invalid Explicit Notice Case", - policy: Policy{Value: "1y--"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the explicit notice", + description: "Malformed Regs.Ext", + consent: "anyConsent", + regs: &openrtb.Regs{Ext: json.RawMessage(`malformed`)}, + expectedError: true, }, + } + + for _, test := range testCases { + result, err := buildRegsWrite(test.consent, test.regs) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, result, test.description) + } +} + +func TestBuildExt(t *testing.T) { + testCases := []struct { + description string + noSaleBidders []string + ext json.RawMessage + expected json.RawMessage + expectedError bool + }{ { - description: "Invalid Opt-Out Sale Char", - policy: Policy{Value: "1-X-"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Clear - Nil", + noSaleBidders: nil, + ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), + expected: nil, }, { - description: "Invalid Opt-Out Sale Case", - policy: Policy{Value: "1-y-"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Clear - Empty", + noSaleBidders: []string{}, + ext: json.RawMessage(`{"prebid":{"nosale":["a", "b"]}}`), + expected: nil, }, { - description: "Invalid LSPA Char", - policy: Policy{Value: "1--X"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Clear - Error", + noSaleBidders: []string{}, + ext: json.RawMessage(`malformed`), + expectedError: true, }, { - description: "Invalid LSPA Case", - policy: Policy{Value: "1--y"}, - expectedError: "request.regs.ext.us_privacy must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Write", + noSaleBidders: []string{"a", "b"}, + ext: nil, + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), + }, + { + description: "Write - Error", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`malformed`), + expectedError: true, }, } for _, test := range testCases { - result := test.policy.Validate() - - if test.expectedError == "" { - assert.NoError(t, result, test.description) - } else { - assert.EqualError(t, result, test.expectedError, test.description) - } + result, err := buildExt(test.noSaleBidders, test.ext) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, result, test.description) } } -func TestValidateConsent(t *testing.T) { +func TestBuildExtClear(t *testing.T) { testCases := []struct { description string - consent string - expectedError string + ext json.RawMessage + expected json.RawMessage + expectedError bool }{ { - description: "Valid", - consent: "1NYN", - expectedError: "", + description: "Nil Ext", + ext: nil, + expected: nil, }, { - description: "Valid - Not Applicable", - consent: "1---", - expectedError: "", + description: "Empty Ext", + ext: json.RawMessage(``), + expected: json.RawMessage(``), }, { - description: "Invalid Empty", - consent: "", - expectedError: "", + description: "Empty Ext Object", + ext: json.RawMessage(`{}`), + expected: json.RawMessage(`{}`), }, { - description: "Invalid Length", - consent: "1NY", - expectedError: "must contain 4 characters", + description: "Empty Ext.Prebid", + ext: json.RawMessage(`{"prebid":{}}`), + expected: nil, }, { - description: "Invalid Version", - consent: "2---", - expectedError: "must specify version 1", + description: "Removes Ext Entirely", + ext: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), + expected: nil, }, { - description: "Invalid Explicit Notice Char", - consent: "1X--", - expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + description: "Leaves Other Ext Values", + ext: json.RawMessage(`{"other":"any","prebid":{"nosale":["a","b"]}}`), + expected: json.RawMessage(`{"other":"any"}`), }, { - description: "Invalid Explicit Notice Case", - consent: "1y--", - expectedError: "must specify 'N', 'Y', or '-' for the explicit notice", + description: "Leaves Other Ext.Prebid Values", + ext: json.RawMessage(`{"prebid":{"nosale":["a","b"],"other":"any"}}`), + expected: json.RawMessage(`{"prebid":{"other":"any"}}`), }, { - description: "Invalid Opt-Out Sale Char", - consent: "1-X-", - expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Leaves All Other Values", + ext: json.RawMessage(`{"other":"ABC","prebid":{"nosale":["a","b"],"other":"123"}}`), + expected: json.RawMessage(`{"other":"ABC","prebid":{"other":"123"}}`), }, { - description: "Invalid Opt-Out Sale Case", - consent: "1-y-", - expectedError: "must specify 'N', 'Y', or '-' for the opt-out sale", + description: "Malformed Ext", + ext: json.RawMessage(`malformed`), + expectedError: true, }, { - description: "Invalid LSPA Char", - consent: "1--X", - expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Malformed Ext.Prebid", + ext: json.RawMessage(`{"prebid":malformed}`), + expectedError: true, }, { - description: "Invalid LSPA Case", - consent: "1--y", - expectedError: "must specify 'N', 'Y', or '-' for the limited service provider agreement", + description: "Invalid Ext.Prebid Type", + ext: json.RawMessage(`{"prebid":123}`), + expectedError: true, }, } for _, test := range testCases { - result := ValidateConsent(test.consent) - - if test.expectedError == "" { - assert.NoError(t, result, test.description) - } else { - assert.EqualError(t, result, test.expectedError, test.description) - } + result, err := buildExtClear(test.ext) + assertError(t, test.expectedError, err, test.description) + assert.Equal(t, test.expected, result, test.description) } } -func TestShouldEnforce(t *testing.T) { +func TestBuildExtWrite(t *testing.T) { testCases := []struct { - description string - policy Policy - expected bool + description string + noSaleBidders []string + ext json.RawMessage + expected json.RawMessage + expectedError bool }{ { - description: "Enforceable", - policy: Policy{Value: "1-Y-"}, - expected: true, + description: "Nil Ext", + noSaleBidders: []string{"a", "b"}, + ext: nil, + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), + }, + { + description: "Empty Ext", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(``), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), }, { - description: "Not Enforceable - Not Present", - policy: Policy{Value: ""}, - expected: false, + description: "Empty Ext Object", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{}`), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), }, { - description: "Not Enforceable - Opt-Out Unknown", - policy: Policy{Value: "1---"}, - expected: false, + description: "Empty Ext.Prebid", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":{}}`), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), }, { - description: "Not Enforceable - Opt-Out Explicitly No", - policy: Policy{Value: "1-N-"}, - expected: false, + description: "Overwrites Existing", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":{"nosale":["x","y"]}}`), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), }, { - description: "Invalid", - policy: Policy{Value: "2---"}, - expected: false, + description: "Leaves Other Ext Values", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"other":"any"}`), + expected: json.RawMessage(`{"other":"any","prebid":{"nosale":["a","b"]}}`), + }, + { + description: "Leaves Other Ext.Prebid Values", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":{"other":"any"}}`), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"],"other":"any"}}`), + }, + { + description: "Leaves All Other Values", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"other":"ABC","prebid":{"other":"123"}}`), + expected: json.RawMessage(`{"other":"ABC","prebid":{"nosale":["a","b"],"other":"123"}}`), + }, + { + description: "Invalid Ext.Prebid No Sale Type - Still Overrides", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":{"nosale":123}}`), + expected: json.RawMessage(`{"prebid":{"nosale":["a","b"]}}`), + }, + { + description: "Invalid Ext.Prebid Type ", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":"wrongtype"}`), + expectedError: true, + }, + { + description: "Malformed Ext", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{malformed`), + expectedError: true, + }, + { + description: "Malformed Ext.Prebid", + noSaleBidders: []string{"a", "b"}, + ext: json.RawMessage(`{"prebid":malformed}`), + expectedError: true, }, } for _, test := range testCases { - result := test.policy.ShouldEnforce() + result, err := buildExtWrite(test.noSaleBidders, test.ext) + assertError(t, test.expectedError, err, test.description) assert.Equal(t, test.expected, result, test.description) } } + +func assertError(t *testing.T, expectError bool, err error, description string) { + t.Helper() + if expectError { + assert.Error(t, err, description) + } else { + assert.NoError(t, err, description) + } +} diff --git a/privacy/enforcer.go b/privacy/enforcer.go new file mode 100644 index 00000000000..0d5ecad5309 --- /dev/null +++ b/privacy/enforcer.go @@ -0,0 +1,43 @@ +package privacy + +// PolicyEnforcer determines if personally identifiable information (PII) should be removed or anonymized per the policy. +type PolicyEnforcer interface { + // CanEnforce returns true when policy information is specifically provided by the publisher. + CanEnforce() bool + + // ShouldEnforce returns true when the OpenRTB request should have personally identifiable + // information (PII) removed or anonymized per the policy. + ShouldEnforce(bidder string) bool +} + +// NilPolicyEnforcer implements the PolicyEnforcer interface but will always return false. +type NilPolicyEnforcer struct{} + +// CanEnforce is hardcoded to always return false. +func (NilPolicyEnforcer) CanEnforce() bool { + return false +} + +// ShouldEnforce is hardcoded to always return false. +func (NilPolicyEnforcer) ShouldEnforce(bidder string) bool { + return false +} + +// EnabledPolicyEnforcer decorates a PolicyEnforcer with an enabled flag. +type EnabledPolicyEnforcer struct { + Enabled bool + PolicyEnforcer PolicyEnforcer +} + +// CanEnforce returns true when the PolicyEnforcer can enforce. +func (p EnabledPolicyEnforcer) CanEnforce() bool { + return p.PolicyEnforcer.CanEnforce() +} + +// ShouldEnforce returns true when the enforcer is enabled the PolicyEnforcer allows enforcement. +func (p EnabledPolicyEnforcer) ShouldEnforce(bidder string) bool { + if p.Enabled { + return p.PolicyEnforcer.ShouldEnforce(bidder) + } + return false +} diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go new file mode 100644 index 00000000000..b0c4032c714 --- /dev/null +++ b/privacy/enforcer_test.go @@ -0,0 +1,18 @@ +package privacy + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNilEnforcerCanEnforce(t *testing.T) { + nilEnforcer := &NilPolicyEnforcer{} + assert.False(t, nilEnforcer.CanEnforce()) +} + +func TestNilEnforcerShouldEnforce(t *testing.T) { + nilEnforcer := &NilPolicyEnforcer{} + assert.False(t, nilEnforcer.ShouldEnforce("")) + assert.False(t, nilEnforcer.ShouldEnforce("anyBidder")) +} diff --git a/privacy/gdpr/consentwriter.go b/privacy/gdpr/consentwriter.go new file mode 100644 index 00000000000..040bbd6c94b --- /dev/null +++ b/privacy/gdpr/consentwriter.go @@ -0,0 +1,44 @@ +package gdpr + +import ( + "encoding/json" + + "github.com/prebid/prebid-server/openrtb_ext" + + "github.com/mxmCherry/openrtb" +) + +// ConsentWriter implements the PolicyWriter interface for GDPR TCF. +type ConsentWriter struct { + Consent string +} + +// Write mutates an OpenRTB bid request with the GDPR TCF consent. +func (c ConsentWriter) Write(req *openrtb.BidRequest) error { + if c.Consent == "" { + return nil + } + + if req.User == nil { + req.User = &openrtb.User{} + } + + if req.User.Ext == nil { + ext, err := json.Marshal(openrtb_ext.ExtUser{Consent: c.Consent}) + if err == nil { + req.User.Ext = ext + } + return err + } + + var extMap map[string]interface{} + err := json.Unmarshal(req.User.Ext, &extMap) + if err == nil { + extMap["consent"] = c.Consent + ext, err := json.Marshal(extMap) + if err == nil { + req.User.Ext = ext + } + } + return err +} diff --git a/privacy/gdpr/consentwriter_test.go b/privacy/gdpr/consentwriter_test.go new file mode 100644 index 00000000000..77fbdf88d92 --- /dev/null +++ b/privacy/gdpr/consentwriter_test.go @@ -0,0 +1,101 @@ +package gdpr + +import ( + "encoding/json" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/stretchr/testify/assert" +) + +func TestConsentWriter(t *testing.T) { + testCases := []struct { + description string + consent string + request *openrtb.BidRequest + expected *openrtb.BidRequest + expectedError bool + }{ + { + description: "Empty", + consent: "", + request: &openrtb.BidRequest{}, + expected: &openrtb.BidRequest{}, + }, + { + description: "Enabled With Nil Request User Object", + consent: "anyConsent", + request: &openrtb.BidRequest{}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"anyConsent"}`)}}, + }, + { + description: "Enabled With Nil Request User Ext Object", + consent: "anyConsent", + request: &openrtb.BidRequest{User: &openrtb.User{}}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"anyConsent"}`)}}, + }, + { + description: "Enabled With Existing Request User Ext Object - Doesn't Overwrite", + consent: "anyConsent", + request: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"existing":"any"}`)}}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}}, + }, + { + description: "Enabled With Existing Request User Ext Object - Overwrites", + consent: "anyConsent", + request: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"existing":"any","consent":"toBeOverwritten"}`)}}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}}, + }, + { + description: "Enabled With Existing Malformed Request User Ext Object", + consent: "anyConsent", + request: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`malformed`)}}, + expectedError: true, + }, + { + description: "Injection Attack With Nil Request User Object", + consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"", + request: &openrtb.BidRequest{}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), + }}, + }, + { + description: "Injection Attack With Nil Request User Ext Object", + consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"", + request: &openrtb.BidRequest{User: &openrtb.User{}}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), + }}, + }, + { + description: "Injection Attack With Existing Request User Ext Object", + consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"", + request: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"existing":"any"}`), + }}, + expected: &openrtb.BidRequest{User: &openrtb.User{ + Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"","existing":"any"}`), + }}, + }, + } + + for _, test := range testCases { + writer := ConsentWriter{test.consent} + err := writer.Write(test.request) + + if test.expectedError { + assert.Error(t, err, test.description) + } else { + assert.NoError(t, err, test.description) + assert.Equal(t, test.expected, test.request, test.description) + } + } +} diff --git a/privacy/gdpr/policy.go b/privacy/gdpr/policy.go index 4733e1edd38..0464a9ff979 100644 --- a/privacy/gdpr/policy.go +++ b/privacy/gdpr/policy.go @@ -1,10 +1,6 @@ package gdpr import ( - "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" - - "github.com/mxmCherry/openrtb" "github.com/prebid/go-gdpr/vendorconsent" ) @@ -14,38 +10,8 @@ type Policy struct { Consent string } -// Write mutates an OpenRTB bid request with the context of the GDPR policy. -func (p Policy) Write(req *openrtb.BidRequest) error { - if p.Consent == "" { - return nil - } - - if req.User == nil { - req.User = &openrtb.User{} - } - - if req.User.Ext == nil { - ext, err := json.Marshal(openrtb_ext.ExtUser{Consent: p.Consent}) - if err == nil { - req.User.Ext = ext - } - return err - } - - var extMap map[string]interface{} - err := json.Unmarshal(req.User.Ext, &extMap) - if err == nil { - extMap["consent"] = p.Consent - ext, err := json.Marshal(extMap) - if err == nil { - req.User.Ext = ext - } - } - return err -} - -// ValidateConsent returns an error if the GDPR consent string does not adhere to the IAB TCF spec. -func ValidateConsent(consent string) error { +// ValidateConsent returns true if the consent string is empty or valid per the IAB TCF spec. +func ValidateConsent(consent string) bool { _, err := vendorconsent.ParseString(consent) - return err + return err == nil } diff --git a/privacy/gdpr/policy_test.go b/privacy/gdpr/policy_test.go index c9bf10cd24a..dc8f56425c5 100644 --- a/privacy/gdpr/policy_test.go +++ b/privacy/gdpr/policy_test.go @@ -1,129 +1,36 @@ package gdpr import ( - "encoding/json" "testing" - "github.com/mxmCherry/openrtb" "github.com/stretchr/testify/assert" ) -func TestWrite(t *testing.T) { - testCases := []struct { - description string - policy Policy - request *openrtb.BidRequest - expected *openrtb.BidRequest - expectedError bool - }{ - { - description: "Disabled", - policy: Policy{Consent: ""}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{}, - }, - { - description: "Enabled With Nil Request User Object", - policy: Policy{Consent: "anyConsent"}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"anyConsent"}`)}}, - }, - { - description: "Enabled With Nil Request User Ext Object", - policy: Policy{Consent: "anyConsent"}, - request: &openrtb.BidRequest{User: &openrtb.User{}}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"anyConsent"}`)}}, - }, - { - description: "Enabled With Existing Request User Ext Object - Doesn't Overwrite", - policy: Policy{Consent: "anyConsent"}, - request: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"existing":"any"}`)}}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}}, - }, - { - description: "Enabled With Existing Request User Ext Object - Overwrites", - policy: Policy{Consent: "anyConsent"}, - request: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"existing":"any","consent":"toBeOverwritten"}`)}}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"anyConsent","existing":"any"}`)}}, - }, - { - description: "Enabled With Existing Malformed Request User Ext Object", - policy: Policy{Consent: "anyConsent"}, - request: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`malformed`)}}, - expectedError: true, - }, - { - description: "Injection Attack With Nil Request User Object", - policy: Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }}, - }, - { - description: "Injection Attack With Nil Request User Ext Object", - policy: Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{User: &openrtb.User{}}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}`), - }}, - }, - { - description: "Injection Attack With Existing Request User Ext Object", - policy: Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\""}, - request: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"existing":"any"}`), - }}, - expected: &openrtb.BidRequest{User: &openrtb.User{ - Ext: json.RawMessage(`{"consent":"BONV8oqONXwgmADACHENAO7pqzAAppY\"},\"oops\":\"malicious\",\"p\":{\"p\":\"","existing":"any"}`), - }}, - }, - } - - for _, test := range testCases { - err := test.policy.Write(test.request) - - if test.expectedError { - assert.Error(t, err, test.description) - } else { - assert.NoError(t, err, test.description) - assert.Equal(t, test.expected, test.request, test.description) - } - } -} - func TestValidateConsent(t *testing.T) { testCases := []struct { description string consent string - expectError bool + expected bool }{ { description: "Invalid", consent: "", - expectError: true, + expected: false, }, { - description: "Valid", + description: "TCF1 Valid", consent: "BONV8oqONXwgmADACHENAO7pqzAAppY", - expectError: false, + expected: true, + }, + { + description: "TCF2 Valid", + consent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + expected: true, }, } for _, test := range testCases { result := ValidateConsent(test.consent) - - if test.expectError { - assert.Error(t, result, test.description) - } else { - assert.NoError(t, result, test.description) - } + assert.Equal(t, test.expected, result, test.description) } } diff --git a/privacy/lmt/policy.go b/privacy/lmt/policy.go index 79425bf59f7..295dcc46469 100644 --- a/privacy/lmt/policy.go +++ b/privacy/lmt/policy.go @@ -15,19 +15,21 @@ type Policy struct { SignalProvided bool } -// ReadPolicy extracts the LMT (Limit Ad Tracking) policy from an OpenRTB bid request. -func ReadPolicy(req *openrtb.BidRequest) Policy { - policy := Policy{} - +// ReadFromRequest extracts the LMT (Limit Ad Tracking) policy from an OpenRTB bid request. +func ReadFromRequest(req *openrtb.BidRequest) (policy Policy) { if req != nil && req.Device != nil && req.Device.Lmt != nil { policy.Signal = int(*req.Device.Lmt) policy.SignalProvided = true } + return +} - return policy +// CanEnforce returns true the LMT (Limit Ad Tracking) signal is provided by the publisher. +func (p Policy) CanEnforce() bool { + return p.SignalProvided } // ShouldEnforce returns true when the LMT (Limit Ad Tracking) policy is in effect. -func (p Policy) ShouldEnforce() bool { +func (p Policy) ShouldEnforce(bidder string) bool { return p.SignalProvided && p.Signal == trackingRestricted } diff --git a/privacy/lmt/policy_test.go b/privacy/lmt/policy_test.go index 45de219a9bf..3027414fd02 100644 --- a/privacy/lmt/policy_test.go +++ b/privacy/lmt/policy_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRead(t *testing.T) { +func TestReadFromRequest(t *testing.T) { var one int8 = 1 testCases := []struct { @@ -60,11 +60,73 @@ func TestRead(t *testing.T) { } for _, test := range testCases { - p := ReadPolicy(test.request) + p := ReadFromRequest(test.request) assert.Equal(t, test.expectedPolicy, p, test.description) } } +func TestCanEnforce(t *testing.T) { + testCases := []struct { + description string + policy Policy + expected bool + }{ + { + description: "Signal Not Provided - Zero", + policy: Policy{ + Signal: 0, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Not Provided - One", + policy: Policy{ + Signal: 1, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Not Provided - Other", + policy: Policy{ + Signal: 42, + SignalProvided: false, + }, + expected: false, + }, + { + description: "Signal Provided - Zero", + policy: Policy{ + Signal: 0, + SignalProvided: true, + }, + expected: true, + }, + { + description: "Signal Provided - One", + policy: Policy{ + Signal: 1, + SignalProvided: true, + }, + expected: true, + }, + { + description: "Signal Provided - Other", + policy: Policy{ + Signal: 42, + SignalProvided: true, + }, + expected: true, + }, + } + + for _, test := range testCases { + result := test.policy.CanEnforce() + assert.Equal(t, test.expected, result, test.description) + } +} + func TestShouldEnforce(t *testing.T) { testCases := []struct { description string @@ -122,7 +184,7 @@ func TestShouldEnforce(t *testing.T) { } for _, test := range testCases { - result := test.policy.ShouldEnforce() + result := test.policy.ShouldEnforce("") assert.Equal(t, test.expected, result, test.description) } } diff --git a/privacy/policies.go b/privacy/policies.go index cb11c6d03a6..bc844a4e463 100644 --- a/privacy/policies.go +++ b/privacy/policies.go @@ -1,60 +1,14 @@ package privacy import ( - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/privacy/ccpa" "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/prebid/prebid-server/privacy/lmt" ) // Policies represents the privacy regulations for an OpenRTB bid request. type Policies struct { - GDPR gdpr.Policy CCPA ccpa.Policy -} - -type policyWriter interface { - Write(req *openrtb.BidRequest) error -} - -// Write mutates an OpenRTB bid request with the policies applied. -func (p Policies) Write(req *openrtb.BidRequest) error { - return writePolicies(req, []policyWriter{ - p.GDPR, p.CCPA, - }) -} - -func writePolicies(req *openrtb.BidRequest, writers []policyWriter) error { - for _, writer := range writers { - if err := writer.Write(req); err != nil { - return err - } - } - - return nil -} - -// ReadPoliciesFromConsent inspects the consent string kind and sets the corresponding values in a new Policies object. -func ReadPoliciesFromConsent(consent string) (Policies, bool) { - if len(consent) == 0 { - return Policies{}, false - } - - if err := gdpr.ValidateConsent(consent); err == nil { - return Policies{ - GDPR: gdpr.Policy{ - Consent: consent, - }, - }, true - } - - if err := ccpa.ValidateConsent(consent); err == nil { - return Policies{ - CCPA: ccpa.Policy{ - Value: consent, - }, - }, true - } - - return Policies{}, false + GDPR gdpr.Policy + LMT lmt.Policy } diff --git a/privacy/policies_test.go b/privacy/policies_test.go deleted file mode 100644 index 34fbe52d0e9..00000000000 --- a/privacy/policies_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package privacy - -import ( - "errors" - "testing" - - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestWritePoliciesNone(t *testing.T) { - request := &openrtb.BidRequest{} - policyWriters := []policyWriter{} - - err := writePolicies(request, policyWriters) - - assert.NoError(t, err) -} - -func TestWritePoliciesOne(t *testing.T) { - request := &openrtb.BidRequest{} - mockWriter := new(mockPolicyWriter) - policyWriters := []policyWriter{ - mockWriter, - } - - mockWriter.On("Write", request).Return(nil).Once() - - err := writePolicies(request, policyWriters) - - assert.NoError(t, err) - mockWriter.AssertExpectations(t) -} - -func TestWritePoliciesMany(t *testing.T) { - request := &openrtb.BidRequest{} - mockWriter1 := new(mockPolicyWriter) - mockWriter2 := new(mockPolicyWriter) - policyWriters := []policyWriter{ - mockWriter1, mockWriter2, - } - - mockWriter1.On("Write", request).Return(nil).Once() - mockWriter2.On("Write", request).Return(nil).Once() - - err := writePolicies(request, policyWriters) - - assert.NoError(t, err) - mockWriter1.AssertExpectations(t) - mockWriter2.AssertExpectations(t) -} - -func TestWritePoliciesError(t *testing.T) { - request := &openrtb.BidRequest{} - mockWriter := new(mockPolicyWriter) - policyWriters := []policyWriter{ - mockWriter, - } - - expectedErr := errors.New("anyError") - mockWriter.On("Write", request).Return(expectedErr).Once() - - err := writePolicies(request, policyWriters) - - assert.Error(t, err, expectedErr) - mockWriter.AssertExpectations(t) -} - -type mockPolicyWriter struct { - mock.Mock -} - -func (m *mockPolicyWriter) Write(req *openrtb.BidRequest) error { - args := m.Called(req) - return args.Error(0) -} - -func TestReadPoliciesFromConsent(t *testing.T) { - testCases := []struct { - description string - consent string - expectedResultValue Policies - expectedResultOK bool - }{ - { - description: "Empty String", - consent: "", - expectedResultValue: Policies{}, - expectedResultOK: false, - }, - { - description: "CCPA", - consent: "1NYN", - expectedResultValue: Policies{CCPA: ccpa.Policy{Value: "1NYN"}}, - expectedResultOK: true, - }, - { - description: "GDPR TCF 1.0", - consent: "BONV8oqONXwgmADACHENAO7pqzAAppY", - expectedResultValue: Policies{GDPR: gdpr.Policy{Consent: "BONV8oqONXwgmADACHENAO7pqzAAppY"}}, - expectedResultOK: true, - }, - { - description: "Invalid", - consent: "any invalid", - expectedResultValue: Policies{}, - expectedResultOK: false, - }, - } - - for _, test := range testCases { - resultValue, resultOK := ReadPoliciesFromConsent(test.consent) - assert.Equal(t, test.expectedResultValue, resultValue, test.description+":value") - assert.Equal(t, test.expectedResultOK, resultOK, test.description+":ok") - } -} diff --git a/privacy/writer.go b/privacy/writer.go new file mode 100644 index 00000000000..c61767f16c8 --- /dev/null +++ b/privacy/writer.go @@ -0,0 +1,18 @@ +package privacy + +import ( + "github.com/mxmCherry/openrtb" +) + +// PolicyWriter mutates an OpenRTB bid request with a policy's regulatory information. +type PolicyWriter interface { + Write(req *openrtb.BidRequest) error +} + +// NilPolicyWriter implements the PolicyWriter interface but performs no action. +type NilPolicyWriter struct{} + +// Write is hardcoded to perform no action with the OpenRTB bid request. +func (NilPolicyWriter) Write(req *openrtb.BidRequest) error { + return nil +} diff --git a/privacy/writer_test.go b/privacy/writer_test.go new file mode 100644 index 00000000000..79170cfc451 --- /dev/null +++ b/privacy/writer_test.go @@ -0,0 +1,25 @@ +package privacy + +import ( + "encoding/json" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/stretchr/testify/assert" +) + +func TestNilWriter(t *testing.T) { + request := &openrtb.BidRequest{ + ID: "anyID", + Ext: json.RawMessage(`{"anyJson":"anyValue"}`), + } + expectedRequest := &openrtb.BidRequest{ + ID: "anyID", + Ext: json.RawMessage(`{"anyJson":"anyValue"}`), + } + + nilWriter := &NilPolicyWriter{} + nilWriter.Write(request) + + assert.Equal(t, expectedRequest, request) +} From 472c7a00105cb986c072047dd416af299b7f7a1b Mon Sep 17 00:00:00 2001 From: Scott Kay Date: Thu, 17 Sep 2020 11:05:24 -0400 Subject: [PATCH 203/318] Fix Merge Conflict (#1502) --- adapters/colossus/usersync_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/colossus/usersync_test.go b/adapters/colossus/usersync_test.go index 79d5483d528..52eb6389b1c 100644 --- a/adapters/colossus/usersync_test.go +++ b/adapters/colossus/usersync_test.go @@ -23,7 +23,7 @@ func TestColossusSyncer(t *testing.T) { Consent: "A", }, CCPA: ccpa.Policy{ - Value: "1-YY", + Consent: "1-YY", }, }) From 97be47dc634d8b2fbf70c15fa1d73dc7cbf9d05d Mon Sep 17 00:00:00 2001 From: johnwier <49074029+johnwier@users.noreply.github.com> Date: Thu, 17 Sep 2020 08:29:14 -0700 Subject: [PATCH 204/318] Update conversant adapter for new prebid-server interface (#1484) --- adapters/conversant/cnvr_legacy.go | 291 ++++++ adapters/conversant/cnvr_legacy_test.go | 853 ++++++++++++++++++ adapters/conversant/conversant.go | 359 +++----- adapters/conversant/conversant_test.go | 849 +---------------- .../conversanttest/exemplary/banner.json | 113 +++ .../conversanttest/exemplary/simple_app.json | 114 +++ .../conversanttest/exemplary/video.json | 138 +++ .../supplemental/missing_cnvrext.json | 32 + .../supplemental/missing_ext.json | 30 + .../supplemental/missing_siteid.json | 35 + .../supplemental/server_badresponse.json | 57 ++ .../supplemental/server_nocontent.json | 51 ++ .../supplemental/server_unknownstatus.json | 55 ++ exchange/adapter_map.go | 3 +- openrtb_ext/imp_conversant.go | 13 + static/bidder-params/conversant.json | 4 - 16 files changed, 1908 insertions(+), 1089 deletions(-) create mode 100644 adapters/conversant/cnvr_legacy.go create mode 100644 adapters/conversant/cnvr_legacy_test.go create mode 100644 adapters/conversant/conversanttest/exemplary/banner.json create mode 100644 adapters/conversant/conversanttest/exemplary/simple_app.json create mode 100644 adapters/conversant/conversanttest/exemplary/video.json create mode 100644 adapters/conversant/conversanttest/supplemental/missing_cnvrext.json create mode 100644 adapters/conversant/conversanttest/supplemental/missing_ext.json create mode 100644 adapters/conversant/conversanttest/supplemental/missing_siteid.json create mode 100644 adapters/conversant/conversanttest/supplemental/server_badresponse.json create mode 100644 adapters/conversant/conversanttest/supplemental/server_nocontent.json create mode 100644 adapters/conversant/conversanttest/supplemental/server_unknownstatus.json create mode 100644 openrtb_ext/imp_conversant.go diff --git a/adapters/conversant/cnvr_legacy.go b/adapters/conversant/cnvr_legacy.go new file mode 100644 index 00000000000..bd121f098c0 --- /dev/null +++ b/adapters/conversant/cnvr_legacy.go @@ -0,0 +1,291 @@ +package conversant + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" +) + +type ConversantLegacyAdapter struct { + http *adapters.HTTPAdapter + URI string +} + +// Corresponds to the bidder name in cookies and requests +func (a *ConversantLegacyAdapter) Name() string { + return "conversant" +} + +// Return true so no request will be sent unless user has been sync'ed. +func (a *ConversantLegacyAdapter) SkipNoCookies() bool { + return true +} + +type conversantParams struct { + SiteID string `json:"site_id"` + Secure *int8 `json:"secure"` + TagID string `json:"tag_id"` + Position *int8 `json:"position"` + BidFloor float64 `json:"bidfloor"` + Mobile *int8 `json:"mobile"` + MIMEs []string `json:"mimes"` + API []int8 `json:"api"` + Protocols []int8 `json:"protocols"` + MaxDuration *int64 `json:"maxduration"` +} + +func (a *ConversantLegacyAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) { + mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER, pbs.MEDIA_TYPE_VIDEO} + cnvrReq, err := adapters.MakeOpenRTBGeneric(req, bidder, a.Name(), mediaTypes) + + if err != nil { + return nil, err + } + + // Create a map of impression objects for both request creation + // and response parsing. + + impMap := make(map[string]*openrtb.Imp, len(cnvrReq.Imp)) + for idx := range cnvrReq.Imp { + impMap[cnvrReq.Imp[idx].ID] = &cnvrReq.Imp[idx] + } + + // Fill in additional info from custom params + + for _, unit := range bidder.AdUnits { + var params conversantParams + + imp := impMap[unit.Code] + if imp == nil { + // Skip ad units that do not have corresponding impressions. + continue + } + + err := json.Unmarshal(unit.Params, ¶ms) + if err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + + // Fill in additional Site info + if params.SiteID != "" { + if cnvrReq.Site != nil { + cnvrReq.Site.ID = params.SiteID + } + if cnvrReq.App != nil { + cnvrReq.App.ID = params.SiteID + } + } + + if params.Mobile != nil && !(cnvrReq.Site == nil) { + cnvrReq.Site.Mobile = *params.Mobile + } + + // Fill in additional impression info + + imp.DisplayManager = "prebid-s2s" + imp.DisplayManagerVer = "1.0.1" + imp.BidFloor = params.BidFloor + imp.TagID = params.TagID + + var position *openrtb.AdPosition + if params.Position != nil { + position = openrtb.AdPosition(*params.Position).Ptr() + } + + if imp.Banner != nil { + imp.Banner.Pos = position + } else if imp.Video != nil { + imp.Video.Pos = position + + if len(params.API) > 0 { + imp.Video.API = make([]openrtb.APIFramework, 0, len(params.API)) + for _, api := range params.API { + imp.Video.API = append(imp.Video.API, openrtb.APIFramework(api)) + } + } + + // Include protocols, mimes, and max duration if specified + // These properties can also be specified in ad unit's video object, + // but are overridden if the custom params object also contains them. + + if len(params.Protocols) > 0 { + imp.Video.Protocols = make([]openrtb.Protocol, 0, len(params.Protocols)) + for _, protocol := range params.Protocols { + imp.Video.Protocols = append(imp.Video.Protocols, openrtb.Protocol(protocol)) + } + } + + if len(params.MIMEs) > 0 { + imp.Video.MIMEs = make([]string, len(params.MIMEs)) + copy(imp.Video.MIMEs, params.MIMEs) + } + + if params.MaxDuration != nil { + imp.Video.MaxDuration = *params.MaxDuration + } + } + + // Take care not to override the global secure flag + + if (imp.Secure == nil || *imp.Secure == 0) && params.Secure != nil { + imp.Secure = params.Secure + } + } + + // Do a quick check on required parameters + + if cnvrReq.Site != nil && cnvrReq.Site.ID == "" { + return nil, &errortypes.BadInput{ + Message: "Missing site id", + } + } + + if cnvrReq.App != nil && cnvrReq.App.ID == "" { + return nil, &errortypes.BadInput{ + Message: "Missing app id", + } + } + + // Start capturing debug info + + debug := &pbs.BidderDebug{ + RequestURI: a.URI, + } + + if cnvrReq.Device == nil { + cnvrReq.Device = &openrtb.Device{} + } + + // Convert request to json to be sent over http + + j, _ := json.Marshal(cnvrReq) + + if req.IsDebug { + debug.RequestBody = string(j) + bidder.Debug = append(bidder.Debug, debug) + } + + httpReq, err := http.NewRequest("POST", a.URI, bytes.NewBuffer(j)) + httpReq.Header.Add("Content-Type", "application/json") + httpReq.Header.Add("Accept", "application/json") + + resp, err := ctxhttp.Do(ctx, a.http.Client, httpReq) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + if req.IsDebug { + debug.StatusCode = resp.StatusCode + } + + if resp.StatusCode == 204 { + return nil, nil + } + + body, err := ioutil.ReadAll(resp.Body) + + if err != nil { + return nil, err + } + + if resp.StatusCode == http.StatusBadRequest { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("HTTP status: %d, body: %s", resp.StatusCode, string(body)), + } + } + + if resp.StatusCode != 200 { + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("HTTP status: %d, body: %s", resp.StatusCode, string(body)), + } + } + + if req.IsDebug { + debug.ResponseBody = string(body) + } + + var bidResp openrtb.BidResponse + + err = json.Unmarshal(body, &bidResp) + if err != nil { + return nil, &errortypes.BadServerResponse{ + Message: err.Error(), + } + } + + bids := make(pbs.PBSBidSlice, 0) + + for _, seatbid := range bidResp.SeatBid { + for _, bid := range seatbid.Bid { + if bid.Price <= 0 { + continue + } + + imp := impMap[bid.ImpID] + if imp == nil { + // All returned bids should have a matching impression + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unknown impression id '%s'", bid.ImpID), + } + } + + bidID := bidder.LookupBidID(bid.ImpID) + if bidID == "" { + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unknown ad unit code '%s'", bid.ImpID), + } + } + + pbsBid := pbs.PBSBid{ + BidID: bidID, + AdUnitCode: bid.ImpID, + Price: bid.Price, + Creative_id: bid.CrID, + BidderCode: bidder.BidderCode, + } + + if imp.Video != nil { + pbsBid.CreativeMediaType = "video" + pbsBid.NURL = bid.AdM // Assign to NURL so it'll be interpreted as a vastUrl + pbsBid.Width = imp.Video.W + pbsBid.Height = imp.Video.H + } else { + pbsBid.CreativeMediaType = "banner" + pbsBid.NURL = bid.NURL + pbsBid.Adm = bid.AdM + pbsBid.Width = bid.W + pbsBid.Height = bid.H + } + + bids = append(bids, &pbsBid) + } + } + + if len(bids) == 0 { + return nil, nil + } + + return bids, nil +} + +func NewConversantAdapter(config *adapters.HTTPAdapterConfig, uri string) *ConversantLegacyAdapter { + a := adapters.NewHTTPAdapter(config) + + return &ConversantLegacyAdapter{ + http: a, + URI: uri, + } +} diff --git a/adapters/conversant/cnvr_legacy_test.go b/adapters/conversant/cnvr_legacy_test.go new file mode 100644 index 00000000000..b958e320dc7 --- /dev/null +++ b/adapters/conversant/cnvr_legacy_test.go @@ -0,0 +1,853 @@ +package conversant + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/cache/dummycache" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/pbs" + "github.com/prebid/prebid-server/usersync" +) + +// Constants + +const ExpectedSiteID string = "12345" +const ExpectedDisplayManager string = "prebid-s2s" +const ExpectedBuyerUID string = "AQECT_o7M1FLbQJK8QFmAQEBAQE" +const ExpectedNURL string = "http://test.dotomi.com" +const ExpectedAdM string = "" +const ExpectedCrID string = "98765" + +const DefaultParam = `{"site_id": "12345"}` + +// Test properties of Adapter interface + +func TestConversantProperties(t *testing.T) { + an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, "someUrl") + + assertNotEqual(t, an.Name(), "", "Missing family name") + assertTrue(t, an.SkipNoCookies(), "SkipNoCookies should be true") +} + +// Test empty bid requests + +func TestConversantEmptyBid(t *testing.T) { + an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, "someUrl") + + ctx := context.TODO() + pbReq := pbs.PBSRequest{} + pbBidder := pbs.PBSBidder{} + _, err := an.Call(ctx, &pbReq, &pbBidder) + assertTrue(t, err != nil, "No error received for an invalid request") +} + +// Test required parameters, which is just the site id for now + +func TestConversantRequiredParameters(t *testing.T) { + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent) + }), + ) + defer server.Close() + + an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, server.URL) + ctx := context.TODO() + + testParams := func(params ...string) (pbs.PBSBidSlice, error) { + req, err := CreateBannerRequest(params...) + if err != nil { + return nil, err + } + return an.Call(ctx, req, req.Bidders[0]) + } + + var err error + + if _, err = testParams(`{}`); err == nil { + t.Fatal("Failed to catch missing site id") + } +} + +// Test handling of 404 + +func TestConversantBadStatus(t *testing.T) { + // Create a test http server that returns after 2 milliseconds + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + }), + ) + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + ctx := context.TODO() + pbReq, err := CreateBannerRequest(DefaultParam) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + assertTrue(t, err != nil, "Failed to catch 404 error") +} + +// Test handling of HTTP timeout + +func TestConversantTimeout(t *testing.T) { + // Create a test http server that returns after 2 milliseconds + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + <-time.After(2 * time.Millisecond) + }), + ) + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + // Create a context that expires before http returns + + ctx, cancel := context.WithTimeout(context.Background(), 0) + defer cancel() + + // Create a basic request + pbReq, err := CreateBannerRequest(DefaultParam) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + // Attempt to process the request, which should hit a timeout + // immediately + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err == nil || err != context.DeadlineExceeded { + t.Fatal("No timeout recevied for timed out request", err) + } +} + +// Test handling of 204 + +func TestConversantNoBid(t *testing.T) { + // Create a test http server that returns after 2 milliseconds + + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent) + }), + ) + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + ctx := context.TODO() + pbReq, err := CreateBannerRequest(DefaultParam) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) + if resp != nil || err != nil { + t.Fatal("Failed to handle empty bid", err) + } +} + +// Verify an outgoing openrtp request is created correctly + +func TestConversantRequestDefault(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + ctx := context.TODO() + pbReq, err := CreateBannerRequest(DefaultParam) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") + imp := &lastReq.Imp[0] + + assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") + assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") + assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") + assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") + assertTrue(t, imp.Video == nil, "Request video should be nil") + assertEqual(t, int(*imp.Secure), 0, "Request secure") + assertEqual(t, imp.BidFloor, 0.0, "Request bid floor") + assertEqual(t, imp.TagID, "", "Request tag id") + assertTrue(t, imp.Banner.Pos == nil, "Request pos") + assertEqual(t, int(*imp.Banner.W), 300, "Request width") + assertEqual(t, int(*imp.Banner.H), 250, "Request height") +} + +// Verify inapp video request +func TestConversantInappVideoRequest(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + requestParam := `{"secure": 1, "site_id": "12345"}` + appParam := `{ "bundle": "com.naver.linewebtoon" }` + videoParam := `{ "mimes": ["video/x-ms-wmv"], + "protocols": [1, 2], + "maxduration": 90 }` + + ctx := context.TODO() + pbReq := CreateRequest(requestParam) + pbReq, err := ConvertToVideoRequest(pbReq, videoParam) + if err != nil { + t.Fatal("failed to parse request") + } + pbReq, err = ConvertToAppRequest(pbReq, appParam) + if err != nil { + t.Fatal("failed to parse request") + } + pbReq, err = ParseRequest(pbReq) + if err != nil { + t.Fatal("failed to parse request") + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + + imp := &lastReq.Imp[0] + assertEqual(t, int(imp.Video.W), 300, "Request width") + assertEqual(t, int(imp.Video.H), 250, "Request height") + assertEqual(t, lastReq.App.ID, "12345", "App Id") +} + +// Verify inapp video request +func TestConversantInappBannerRequest(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "secure": 1, + "site_id": "12345", + "tag_id": "top", + "position": 2, + "bidfloor": 1.01 }` + appParam := `{ "bundle": "com.naver.linewebtoon" }` + + ctx := context.TODO() + pbReq, _ := CreateBannerRequest(param) + pbReq, err := ConvertToAppRequest(pbReq, appParam) + if err != nil { + t.Fatal("failed to parse request") + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + + imp := &lastReq.Imp[0] + assertEqual(t, lastReq.App.ID, "12345", "App Id") + assertEqual(t, int(*imp.Banner.W), 300, "Request width") + assertEqual(t, int(*imp.Banner.H), 250, "Request height") +} + +// Verify an outgoing openrtp request with additional conversant parameters is +// processed correctly + +func TestConversantRequest(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345", + "secure": 1, + "tag_id": "top", + "position": 2, + "bidfloor": 1.01, + "mobile": 1 }` + + ctx := context.TODO() + pbReq, err := CreateBannerRequest(param) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") + imp := &lastReq.Imp[0] + + assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") + assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") + assertEqual(t, int(lastReq.Site.Mobile), 1, "Request site mobile flag") + assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") + assertTrue(t, imp.Video == nil, "Request video should be nil") + assertEqual(t, int(*imp.Secure), 1, "Request secure") + assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") + assertEqual(t, imp.TagID, "top", "Request tag id") + assertEqual(t, int(*imp.Banner.Pos), 2, "Request pos") + assertEqual(t, int(*imp.Banner.W), 300, "Request width") + assertEqual(t, int(*imp.Banner.H), 250, "Request height") +} + +// Verify openrtp responses are converted correctly + +func TestConversantResponse(t *testing.T) { + prices := []float64{0.01, 0.0, 2.01} + server, lastReq := CreateServer(prices...) + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345", + "secure": 1, + "tag_id": "top", + "position": 2, + "bidfloor": 1.01, + "mobile" : 1}` + + ctx := context.TODO() + pbReq, err := CreateBannerRequest(param, param, param) + if err != nil { + t.Fatal("Failed to create a banner request", err) + } + + resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + prices, imps := FilterZeroPrices(prices, lastReq.Imp) + + assertEqual(t, len(resp), len(prices), "Bad number of responses") + + for i, bid := range resp { + assertEqual(t, bid.Price, prices[i], "Bad price in response") + assertEqual(t, bid.AdUnitCode, imps[i].ID, "Bad bid id in response") + + if bid.Price > 0 { + assertEqual(t, bid.Adm, ExpectedAdM, "Bad ad markup in response") + assertEqual(t, bid.NURL, ExpectedNURL, "Bad notification url in response") + assertEqual(t, bid.Creative_id, ExpectedCrID, "Bad creative id in response") + assertEqual(t, bid.Width, *imps[i].Banner.W, "Bad width in response") + assertEqual(t, bid.Height, *imps[i].Banner.H, "Bad height in response") + } + } +} + +// Test video request + +func TestConversantBasicVideoRequest(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345", + "tag_id": "bottom left", + "position": 3, + "bidfloor": 1.01 }` + + ctx := context.TODO() + pbReq, err := CreateVideoRequest(param) + if err != nil { + t.Fatal("Failed to create a video request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") + imp := &lastReq.Imp[0] + + assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") + assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") + assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") + assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") + assertTrue(t, imp.Banner == nil, "Request banner should be nil") + assertEqual(t, int(*imp.Secure), 0, "Request secure") + assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") + assertEqual(t, imp.TagID, "bottom left", "Request tag id") + assertEqual(t, int(*imp.Video.Pos), 3, "Request pos") + assertEqual(t, int(imp.Video.W), 300, "Request width") + assertEqual(t, int(imp.Video.H), 250, "Request height") + + assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") + assertEqual(t, imp.Video.MIMEs[0], "video/mp4", "Requst video MIMEs type") + assertTrue(t, imp.Video.Protocols == nil, "Request video protocols") + assertEqual(t, imp.Video.MaxDuration, int64(0), "Request video 0 max duration") + assertTrue(t, imp.Video.API == nil, "Request video api should be nil") +} + +// Test video request with parameters in custom params object + +func TestConversantVideoRequestWithParams(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345", + "tag_id": "bottom left", + "position": 3, + "bidfloor": 1.01, + "mimes": ["video/x-ms-wmv"], + "protocols": [1, 2], + "api": [1, 2], + "maxduration": 90 }` + + ctx := context.TODO() + pbReq, err := CreateVideoRequest(param) + if err != nil { + t.Fatal("Failed to create a video request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") + imp := &lastReq.Imp[0] + + assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") + assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") + assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") + assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") + assertTrue(t, imp.Banner == nil, "Request banner should be nil") + assertEqual(t, int(*imp.Secure), 0, "Request secure") + assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") + assertEqual(t, imp.TagID, "bottom left", "Request tag id") + assertEqual(t, int(*imp.Video.Pos), 3, "Request pos") + assertEqual(t, int(imp.Video.W), 300, "Request width") + assertEqual(t, int(imp.Video.H), 250, "Request height") + + assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") + assertEqual(t, imp.Video.MIMEs[0], "video/x-ms-wmv", "Requst video MIMEs type") + assertEqual(t, len(imp.Video.Protocols), 2, "Request video protocols") + assertEqual(t, imp.Video.Protocols[0], openrtb.Protocol(1), "Request video protocols 1") + assertEqual(t, imp.Video.Protocols[1], openrtb.Protocol(2), "Request video protocols 2") + assertEqual(t, imp.Video.MaxDuration, int64(90), "Request video 0 max duration") + assertEqual(t, len(imp.Video.API), 2, "Request video api should be nil") + assertEqual(t, imp.Video.API[0], openrtb.APIFramework(1), "Request video api 1") + assertEqual(t, imp.Video.API[1], openrtb.APIFramework(2), "Request video api 2") +} + +// Test video request with parameters in the video object + +func TestConversantVideoRequestWithParams2(t *testing.T) { + server, lastReq := CreateServer() + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345" }` + videoParam := `{ "mimes": ["video/x-ms-wmv"], + "protocols": [1, 2], + "maxduration": 90 }` + + ctx := context.TODO() + pbReq := CreateRequest(param) + pbReq, err := ConvertToVideoRequest(pbReq, videoParam) + if err != nil { + t.Fatal("Failed to convert to a video request", err) + } + pbReq, err = ParseRequest(pbReq) + if err != nil { + t.Fatal("Failed to parse video request", err) + } + + _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") + imp := &lastReq.Imp[0] + + assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") + assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") + assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") + assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") + assertTrue(t, imp.Banner == nil, "Request banner should be nil") + assertEqual(t, int(*imp.Secure), 0, "Request secure") + assertEqual(t, imp.BidFloor, 0.0, "Request bid floor") + assertEqual(t, int(imp.Video.W), 300, "Request width") + assertEqual(t, int(imp.Video.H), 250, "Request height") + + assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") + assertEqual(t, imp.Video.MIMEs[0], "video/x-ms-wmv", "Requst video MIMEs type") + assertEqual(t, len(imp.Video.Protocols), 2, "Request video protocols") + assertEqual(t, imp.Video.Protocols[0], openrtb.Protocol(1), "Request video protocols 1") + assertEqual(t, imp.Video.Protocols[1], openrtb.Protocol(2), "Request video protocols 2") + assertEqual(t, imp.Video.MaxDuration, int64(90), "Request video 0 max duration") +} + +// Test video responses + +func TestConversantVideoResponse(t *testing.T) { + prices := []float64{0.01, 0.0, 2.01} + server, lastReq := CreateServer(prices...) + if server == nil { + t.Fatal("server not created") + } + + defer server.Close() + + // Create a adapter to test + + conf := *adapters.DefaultHTTPAdapterConfig + an := NewConversantAdapter(&conf, server.URL) + + param := `{ "site_id": "12345", + "secure": 1, + "tag_id": "top", + "position": 2, + "bidfloor": 1.01, + "mobile" : 1}` + + ctx := context.TODO() + pbReq, err := CreateVideoRequest(param, param, param) + if err != nil { + t.Fatal("Failed to create a video request", err) + } + + resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) + if err != nil { + t.Fatal("Failed to retrieve bids", err) + } + + prices, imps := FilterZeroPrices(prices, lastReq.Imp) + + assertEqual(t, len(resp), len(prices), "Bad number of responses") + + for i, bid := range resp { + assertEqual(t, bid.Price, prices[i], "Bad price in response") + assertEqual(t, bid.AdUnitCode, imps[i].ID, "Bad bid id in response") + + if bid.Price > 0 { + assertEqual(t, bid.Adm, "", "Bad ad markup in response") + assertEqual(t, bid.NURL, ExpectedAdM, "Bad notification url in response") + assertEqual(t, bid.Creative_id, ExpectedCrID, "Bad creative id in response") + assertEqual(t, bid.Width, imps[i].Video.W, "Bad width in response") + assertEqual(t, bid.Height, imps[i].Video.H, "Bad height in response") + } + } +} + +// Helpers to create a banner and video requests + +func CreateRequest(params ...string) *pbs.PBSRequest { + num := len(params) + + req := pbs.PBSRequest{ + Tid: "t-000", + AccountID: "1", + AdUnits: make([]pbs.AdUnit, num), + } + + for i := 0; i < num; i++ { + req.AdUnits[i] = pbs.AdUnit{ + Code: fmt.Sprintf("au-%03d", i), + Sizes: []openrtb.Format{ + { + W: 300, + H: 250, + }, + }, + Bids: []pbs.Bids{ + { + BidderCode: "conversant", + BidID: fmt.Sprintf("b-%03d", i), + Params: json.RawMessage(params[i]), + }, + }, + } + } + + return &req +} + +// Convert a request to a video request by adding required properties + +func ConvertToVideoRequest(req *pbs.PBSRequest, videoParams ...string) (*pbs.PBSRequest, error) { + for i := 0; i < len(req.AdUnits); i++ { + video := pbs.PBSVideo{} + if i < len(videoParams) { + err := json.Unmarshal([]byte(videoParams[i]), &video) + if err != nil { + return nil, err + } + } + + if video.Mimes == nil { + video.Mimes = []string{"video/mp4"} + } + + req.AdUnits[i].Video = video + req.AdUnits[i].MediaTypes = []string{"video"} + } + + return req, nil +} + +// Convert a request to an app request by adding required properties +func ConvertToAppRequest(req *pbs.PBSRequest, appParams string) (*pbs.PBSRequest, error) { + app := new(openrtb.App) + err := json.Unmarshal([]byte(appParams), &app) + if err == nil { + req.App = app + } + + return req, nil +} + +// Feed the request thru the prebid parser so user id and +// other private properties are defined + +func ParseRequest(req *pbs.PBSRequest) (*pbs.PBSRequest, error) { + body := new(bytes.Buffer) + _ = json.NewEncoder(body).Encode(req) + + // Need to pass the conversant user id thru uid cookie + + httpReq := httptest.NewRequest("POST", "/foo", body) + cookie := usersync.NewPBSCookie() + _ = cookie.TrySync("conversant", ExpectedBuyerUID) + httpReq.Header.Set("Cookie", cookie.ToHTTPCookie(90*24*time.Hour).String()) + httpReq.Header.Add("Referer", "http://example.com") + cache, _ := dummycache.New() + hcc := config.HostCookie{} + + parsedReq, err := pbs.ParsePBSRequest(httpReq, &config.AuctionTimeouts{ + Default: 2000, + Max: 2000, + }, cache, &hcc) + + return parsedReq, err +} + +// A helper to create a banner request + +func CreateBannerRequest(params ...string) (*pbs.PBSRequest, error) { + req := CreateRequest(params...) + req, err := ParseRequest(req) + return req, err +} + +// A helper to create a video request + +func CreateVideoRequest(params ...string) (*pbs.PBSRequest, error) { + req := CreateRequest(params...) + req, err := ConvertToVideoRequest(req) + if err != nil { + return nil, err + } + req, err = ParseRequest(req) + return req, err +} + +// Helper to create a test http server that receives and generate openrtb requests and responses + +func CreateServer(prices ...float64) (*httptest.Server, *openrtb.BidRequest) { + var lastBidRequest openrtb.BidRequest + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + var bidReq openrtb.BidRequest + var price float64 + var bids []openrtb.Bid + var bid openrtb.Bid + + err = json.Unmarshal(body, &bidReq) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + lastBidRequest = bidReq + + for i, imp := range bidReq.Imp { + if i < len(prices) { + price = prices[i] + } else { + price = 0 + } + + if price > 0 { + bid = openrtb.Bid{ + ID: imp.ID, + ImpID: imp.ID, + Price: price, + NURL: ExpectedNURL, + AdM: ExpectedAdM, + CrID: ExpectedCrID, + } + + if imp.Banner != nil { + bid.W = *imp.Banner.W + bid.H = *imp.Banner.H + } else if imp.Video != nil { + bid.W = imp.Video.W + bid.H = imp.Video.H + } + } else { + bid = openrtb.Bid{ + ID: imp.ID, + ImpID: imp.ID, + Price: 0, + } + } + + bids = append(bids, bid) + } + + if len(bids) == 0 { + w.WriteHeader(http.StatusNoContent) + } else { + js, _ := json.Marshal(openrtb.BidResponse{ + ID: bidReq.ID, + SeatBid: []openrtb.SeatBid{ + { + Bid: bids, + }, + }, + }) + + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(js) + } + }), + ) + + return server, &lastBidRequest +} + +// Helper to remove impressions with $0 bids + +func FilterZeroPrices(prices []float64, imps []openrtb.Imp) ([]float64, []openrtb.Imp) { + prices2 := make([]float64, 0) + imps2 := make([]openrtb.Imp, 0) + + for i := range prices { + if prices[i] > 0 { + prices2 = append(prices2, prices[i]) + imps2 = append(imps2, imps[i]) + } + } + + return prices2, imps2 +} + +// Helpers to test equality + +func assertEqual(t *testing.T, actual interface{}, expected interface{}, msg string) { + if expected != actual { + msg = fmt.Sprintf("%s: act(%v) != exp(%v)", msg, actual, expected) + t.Fatal(msg) + } +} + +func assertNotEqual(t *testing.T, actual interface{}, expected interface{}, msg string) { + if expected == actual { + msg = fmt.Sprintf("%s: act(%v) == exp(%v)", msg, actual, expected) + t.Fatal(msg) + } +} + +func assertTrue(t *testing.T, val bool, msg string) { + if val == false { + msg = fmt.Sprintf("%s: is false but should be true", msg) + t.Fatal(msg) + } +} + +func assertFalse(t *testing.T, val bool, msg string) { + if val == true { + msg = fmt.Sprintf("%s: is true but should be false", msg) + t.Fatal(msg) + } +} diff --git a/adapters/conversant/conversant.go b/adapters/conversant/conversant.go index b248e2e9dc1..64ddc38bf1a 100644 --- a/adapters/conversant/conversant.go +++ b/adapters/conversant/conversant.go @@ -1,291 +1,176 @@ package conversant import ( - "bytes" - "context" "encoding/json" "fmt" - "io/ioutil" - "net/http" - "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/pbs" - "golang.org/x/net/context/ctxhttp" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" ) type ConversantAdapter struct { - http *adapters.HTTPAdapter - URI string -} - -// Corresponds to the bidder name in cookies and requests -func (a *ConversantAdapter) Name() string { - return "conversant" + URI string } -// Return true so no request will be sent unless user has been sync'ed. -func (a *ConversantAdapter) SkipNoCookies() bool { - return true -} - -type conversantParams struct { - SiteID string `json:"site_id"` - Secure *int8 `json:"secure"` - TagID string `json:"tag_id"` - Position *int8 `json:"position"` - BidFloor float64 `json:"bidfloor"` - Mobile *int8 `json:"mobile"` - MIMEs []string `json:"mimes"` - API []int8 `json:"api"` - Protocols []int8 `json:"protocols"` - MaxDuration *int64 `json:"maxduration"` -} - -func (a *ConversantAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) { - mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER, pbs.MEDIA_TYPE_VIDEO} - cnvrReq, err := adapters.MakeOpenRTBGeneric(req, bidder, a.Name(), mediaTypes) - - if err != nil { - return nil, err - } - - // Create a map of impression objects for both request creation - // and response parsing. - - impMap := make(map[string]*openrtb.Imp, len(cnvrReq.Imp)) - for idx := range cnvrReq.Imp { - impMap[cnvrReq.Imp[idx].ID] = &cnvrReq.Imp[idx] - } - - // Fill in additional info from custom params - - for _, unit := range bidder.AdUnits { - var params conversantParams - - imp := impMap[unit.Code] - if imp == nil { - // Skip ad units that do not have corresponding impressions. - continue - } - - err := json.Unmarshal(unit.Params, ¶ms) - if err != nil { - return nil, &errortypes.BadInput{ - Message: err.Error(), - } +func (c ConversantAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + for i := 0; i < len(request.Imp); i++ { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(request.Imp[i].Ext, &bidderExt); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Impression[%d] missing ext object", i), + }} } - // Fill in additional Site info - if params.SiteID != "" { - if cnvrReq.Site != nil { - cnvrReq.Site.ID = params.SiteID - } - if cnvrReq.App != nil { - cnvrReq.App.ID = params.SiteID - } + var cnvrExt openrtb_ext.ExtImpConversant + if err := json.Unmarshal(bidderExt.Bidder, &cnvrExt); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Impression[%d] missing ext.bidder object", i), + }} } - if params.Mobile != nil && !(cnvrReq.Site == nil) { - cnvrReq.Site.Mobile = *params.Mobile + if cnvrExt.SiteID == "" { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Impression[%d] requires ext.bidder.site_id", i), + }} } - // Fill in additional impression info - - imp.DisplayManager = "prebid-s2s" - imp.DisplayManagerVer = "1.0.1" - imp.BidFloor = params.BidFloor - imp.TagID = params.TagID - - var position *openrtb.AdPosition - if params.Position != nil { - position = openrtb.AdPosition(*params.Position).Ptr() + if i == 0 { + if request.Site != nil { + tmpSite := *request.Site + request.Site = &tmpSite + request.Site.ID = cnvrExt.SiteID + } else if request.App != nil { + tmpApp := *request.App + request.App = &tmpApp + request.App.ID = cnvrExt.SiteID + } } + parseCnvrParams(&request.Imp[i], cnvrExt) + } - if imp.Banner != nil { - imp.Banner.Pos = position - } else if imp.Video != nil { - imp.Video.Pos = position + //create the request body + data, err := json.Marshal(request) + if err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Error in packaging request to JSON"), + }} + } + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return []*adapters.RequestData{{ + Method: "POST", + Uri: c.URI, + Body: data, + Headers: headers, + }}, nil +} - if len(params.API) > 0 { - imp.Video.API = make([]openrtb.APIFramework, 0, len(params.API)) - for _, api := range params.API { - imp.Video.API = append(imp.Video.API, openrtb.APIFramework(api)) - } - } +func parseCnvrParams(imp *openrtb.Imp, cnvrExt openrtb_ext.ExtImpConversant) { + imp.DisplayManager = "prebid-s2s" + imp.DisplayManagerVer = "2.0.0" + imp.BidFloor = cnvrExt.BidFloor + imp.TagID = cnvrExt.TagID - // Include protocols, mimes, and max duration if specified - // These properties can also be specified in ad unit's video object, - // but are overridden if the custom params object also contains them. + // Take care not to override the global secure flag + if (imp.Secure == nil || *imp.Secure == 0) && cnvrExt.Secure != nil { + imp.Secure = cnvrExt.Secure + } - if len(params.Protocols) > 0 { - imp.Video.Protocols = make([]openrtb.Protocol, 0, len(params.Protocols)) - for _, protocol := range params.Protocols { - imp.Video.Protocols = append(imp.Video.Protocols, openrtb.Protocol(protocol)) - } - } + var position *openrtb.AdPosition + if cnvrExt.Position != nil { + position = openrtb.AdPosition(*cnvrExt.Position).Ptr() + } + if imp.Banner != nil { + tmpBanner := *imp.Banner + imp.Banner = &tmpBanner + imp.Banner.Pos = position - if len(params.MIMEs) > 0 { - imp.Video.MIMEs = make([]string, len(params.MIMEs)) - copy(imp.Video.MIMEs, params.MIMEs) - } + } else if imp.Video != nil { + tmpVideo := *imp.Video + imp.Video = &tmpVideo + imp.Video.Pos = position - if params.MaxDuration != nil { - imp.Video.MaxDuration = *params.MaxDuration + if len(cnvrExt.API) > 0 { + imp.Video.API = make([]openrtb.APIFramework, 0, len(cnvrExt.API)) + for _, api := range cnvrExt.API { + imp.Video.API = append(imp.Video.API, openrtb.APIFramework(api)) } } - // Take care not to override the global secure flag + // Include protocols, mimes, and max duration if specified + // These properties can also be specified in ad unit's video object, + // but are overridden if the custom params object also contains them. - if (imp.Secure == nil || *imp.Secure == 0) && params.Secure != nil { - imp.Secure = params.Secure + if len(cnvrExt.Protocols) > 0 { + imp.Video.Protocols = make([]openrtb.Protocol, 0, len(cnvrExt.Protocols)) + for _, protocol := range cnvrExt.Protocols { + imp.Video.Protocols = append(imp.Video.Protocols, openrtb.Protocol(protocol)) + } } - } - - // Do a quick check on required parameters - if cnvrReq.Site != nil && cnvrReq.Site.ID == "" { - return nil, &errortypes.BadInput{ - Message: "Missing site id", + if len(cnvrExt.MIMEs) > 0 { + imp.Video.MIMEs = make([]string, len(cnvrExt.MIMEs)) + copy(imp.Video.MIMEs, cnvrExt.MIMEs) } - } - if cnvrReq.App != nil && cnvrReq.App.ID == "" { - return nil, &errortypes.BadInput{ - Message: "Missing app id", + if cnvrExt.MaxDuration != nil { + imp.Video.MaxDuration = *cnvrExt.MaxDuration } } +} - // Start capturing debug info - - debug := &pbs.BidderDebug{ - RequestURI: a.URI, - } - - if cnvrReq.Device == nil { - cnvrReq.Device = &openrtb.Device{} - } - - // Convert request to json to be sent over http - - j, _ := json.Marshal(cnvrReq) - - if req.IsDebug { - debug.RequestBody = string(j) - bidder.Debug = append(bidder.Debug, debug) - } - - httpReq, err := http.NewRequest("POST", a.URI, bytes.NewBuffer(j)) - httpReq.Header.Add("Content-Type", "application/json") - httpReq.Header.Add("Accept", "application/json") - - resp, err := ctxhttp.Do(ctx, a.http.Client, httpReq) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if req.IsDebug { - debug.StatusCode = resp.StatusCode - } - - if resp.StatusCode == 204 { - return nil, nil - } - - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - return nil, err +func (c ConversantAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil // no bid response } - if resp.StatusCode == http.StatusBadRequest { - return nil, &errortypes.BadInput{ - Message: fmt.Sprintf("HTTP status: %d, body: %s", resp.StatusCode, string(body)), - } + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode), + }} } - if resp.StatusCode != 200 { - return nil, &errortypes.BadServerResponse{ - Message: fmt.Sprintf("HTTP status: %d, body: %s", resp.StatusCode, string(body)), - } + var resp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &resp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("bad server response: %d. ", err), + }} } - if req.IsDebug { - debug.ResponseBody = string(body) + if len(resp.SeatBid) == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Empty bid request"), + }} } - var bidResp openrtb.BidResponse - - err = json.Unmarshal(body, &bidResp) - if err != nil { - return nil, &errortypes.BadServerResponse{ - Message: err.Error(), - } + bids := resp.SeatBid[0].Bid + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bids)) + for i := 0; i < len(bids); i++ { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bids[i], + BidType: getBidType(bids[i].ImpID, internalRequest.Imp), + }) } + return bidResponse, nil +} - bids := make(pbs.PBSBidSlice, 0) - - for _, seatbid := range bidResp.SeatBid { - for _, bid := range seatbid.Bid { - if bid.Price <= 0 { - continue - } - - imp := impMap[bid.ImpID] - if imp == nil { - // All returned bids should have a matching impression - return nil, &errortypes.BadServerResponse{ - Message: fmt.Sprintf("Unknown impression id '%s'", bid.ImpID), - } - } - - bidID := bidder.LookupBidID(bid.ImpID) - if bidID == "" { - return nil, &errortypes.BadServerResponse{ - Message: fmt.Sprintf("Unknown ad unit code '%s'", bid.ImpID), - } - } - - pbsBid := pbs.PBSBid{ - BidID: bidID, - AdUnitCode: bid.ImpID, - Price: bid.Price, - Creative_id: bid.CrID, - BidderCode: bidder.BidderCode, - } - +func getBidType(impId string, imps []openrtb.Imp) openrtb_ext.BidType { + bidType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impId { if imp.Video != nil { - pbsBid.CreativeMediaType = "video" - pbsBid.NURL = bid.AdM // Assign to NURL so it'll be interpreted as a vastUrl - pbsBid.Width = imp.Video.W - pbsBid.Height = imp.Video.H - } else { - pbsBid.CreativeMediaType = "banner" - pbsBid.NURL = bid.NURL - pbsBid.Adm = bid.AdM - pbsBid.Width = bid.W - pbsBid.Height = bid.H + bidType = openrtb_ext.BidTypeVideo } - - bids = append(bids, &pbsBid) + break } } - - if len(bids) == 0 { - return nil, nil - } - - return bids, nil + return bidType } -func NewConversantAdapter(config *adapters.HTTPAdapterConfig, uri string) *ConversantAdapter { - a := adapters.NewHTTPAdapter(config) - - return &ConversantAdapter{ - http: a, - URI: uri, - } +func NewConversantBidder(endpoint string) *ConversantAdapter { + return &ConversantAdapter{URI: endpoint} } diff --git a/adapters/conversant/conversant_test.go b/adapters/conversant/conversant_test.go index b958e320dc7..e3275030e83 100644 --- a/adapters/conversant/conversant_test.go +++ b/adapters/conversant/conversant_test.go @@ -1,853 +1,10 @@ package conversant import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" + "github.com/prebid/prebid-server/adapters/adapterstest" "testing" - "time" - - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" ) -// Constants - -const ExpectedSiteID string = "12345" -const ExpectedDisplayManager string = "prebid-s2s" -const ExpectedBuyerUID string = "AQECT_o7M1FLbQJK8QFmAQEBAQE" -const ExpectedNURL string = "http://test.dotomi.com" -const ExpectedAdM string = "" -const ExpectedCrID string = "98765" - -const DefaultParam = `{"site_id": "12345"}` - -// Test properties of Adapter interface - -func TestConversantProperties(t *testing.T) { - an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, "someUrl") - - assertNotEqual(t, an.Name(), "", "Missing family name") - assertTrue(t, an.SkipNoCookies(), "SkipNoCookies should be true") -} - -// Test empty bid requests - -func TestConversantEmptyBid(t *testing.T) { - an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, "someUrl") - - ctx := context.TODO() - pbReq := pbs.PBSRequest{} - pbBidder := pbs.PBSBidder{} - _, err := an.Call(ctx, &pbReq, &pbBidder) - assertTrue(t, err != nil, "No error received for an invalid request") -} - -// Test required parameters, which is just the site id for now - -func TestConversantRequiredParameters(t *testing.T) { - server := httptest.NewServer( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent) - }), - ) - defer server.Close() - - an := NewConversantAdapter(adapters.DefaultHTTPAdapterConfig, server.URL) - ctx := context.TODO() - - testParams := func(params ...string) (pbs.PBSBidSlice, error) { - req, err := CreateBannerRequest(params...) - if err != nil { - return nil, err - } - return an.Call(ctx, req, req.Bidders[0]) - } - - var err error - - if _, err = testParams(`{}`); err == nil { - t.Fatal("Failed to catch missing site id") - } -} - -// Test handling of 404 - -func TestConversantBadStatus(t *testing.T) { - // Create a test http server that returns after 2 milliseconds - - server := httptest.NewServer( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) - }), - ) - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - ctx := context.TODO() - pbReq, err := CreateBannerRequest(DefaultParam) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - assertTrue(t, err != nil, "Failed to catch 404 error") -} - -// Test handling of HTTP timeout - -func TestConversantTimeout(t *testing.T) { - // Create a test http server that returns after 2 milliseconds - - server := httptest.NewServer( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - <-time.After(2 * time.Millisecond) - }), - ) - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - // Create a context that expires before http returns - - ctx, cancel := context.WithTimeout(context.Background(), 0) - defer cancel() - - // Create a basic request - pbReq, err := CreateBannerRequest(DefaultParam) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - // Attempt to process the request, which should hit a timeout - // immediately - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err == nil || err != context.DeadlineExceeded { - t.Fatal("No timeout recevied for timed out request", err) - } -} - -// Test handling of 204 - -func TestConversantNoBid(t *testing.T) { - // Create a test http server that returns after 2 milliseconds - - server := httptest.NewServer( - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Error(w, http.StatusText(http.StatusNoContent), http.StatusNoContent) - }), - ) - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - ctx := context.TODO() - pbReq, err := CreateBannerRequest(DefaultParam) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) - if resp != nil || err != nil { - t.Fatal("Failed to handle empty bid", err) - } -} - -// Verify an outgoing openrtp request is created correctly - -func TestConversantRequestDefault(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - ctx := context.TODO() - pbReq, err := CreateBannerRequest(DefaultParam) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") - imp := &lastReq.Imp[0] - - assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") - assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") - assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") - assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") - assertTrue(t, imp.Video == nil, "Request video should be nil") - assertEqual(t, int(*imp.Secure), 0, "Request secure") - assertEqual(t, imp.BidFloor, 0.0, "Request bid floor") - assertEqual(t, imp.TagID, "", "Request tag id") - assertTrue(t, imp.Banner.Pos == nil, "Request pos") - assertEqual(t, int(*imp.Banner.W), 300, "Request width") - assertEqual(t, int(*imp.Banner.H), 250, "Request height") -} - -// Verify inapp video request -func TestConversantInappVideoRequest(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - requestParam := `{"secure": 1, "site_id": "12345"}` - appParam := `{ "bundle": "com.naver.linewebtoon" }` - videoParam := `{ "mimes": ["video/x-ms-wmv"], - "protocols": [1, 2], - "maxduration": 90 }` - - ctx := context.TODO() - pbReq := CreateRequest(requestParam) - pbReq, err := ConvertToVideoRequest(pbReq, videoParam) - if err != nil { - t.Fatal("failed to parse request") - } - pbReq, err = ConvertToAppRequest(pbReq, appParam) - if err != nil { - t.Fatal("failed to parse request") - } - pbReq, err = ParseRequest(pbReq) - if err != nil { - t.Fatal("failed to parse request") - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - - imp := &lastReq.Imp[0] - assertEqual(t, int(imp.Video.W), 300, "Request width") - assertEqual(t, int(imp.Video.H), 250, "Request height") - assertEqual(t, lastReq.App.ID, "12345", "App Id") -} - -// Verify inapp video request -func TestConversantInappBannerRequest(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "secure": 1, - "site_id": "12345", - "tag_id": "top", - "position": 2, - "bidfloor": 1.01 }` - appParam := `{ "bundle": "com.naver.linewebtoon" }` - - ctx := context.TODO() - pbReq, _ := CreateBannerRequest(param) - pbReq, err := ConvertToAppRequest(pbReq, appParam) - if err != nil { - t.Fatal("failed to parse request") - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - - imp := &lastReq.Imp[0] - assertEqual(t, lastReq.App.ID, "12345", "App Id") - assertEqual(t, int(*imp.Banner.W), 300, "Request width") - assertEqual(t, int(*imp.Banner.H), 250, "Request height") -} - -// Verify an outgoing openrtp request with additional conversant parameters is -// processed correctly - -func TestConversantRequest(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345", - "secure": 1, - "tag_id": "top", - "position": 2, - "bidfloor": 1.01, - "mobile": 1 }` - - ctx := context.TODO() - pbReq, err := CreateBannerRequest(param) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") - imp := &lastReq.Imp[0] - - assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") - assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") - assertEqual(t, int(lastReq.Site.Mobile), 1, "Request site mobile flag") - assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") - assertTrue(t, imp.Video == nil, "Request video should be nil") - assertEqual(t, int(*imp.Secure), 1, "Request secure") - assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") - assertEqual(t, imp.TagID, "top", "Request tag id") - assertEqual(t, int(*imp.Banner.Pos), 2, "Request pos") - assertEqual(t, int(*imp.Banner.W), 300, "Request width") - assertEqual(t, int(*imp.Banner.H), 250, "Request height") -} - -// Verify openrtp responses are converted correctly - -func TestConversantResponse(t *testing.T) { - prices := []float64{0.01, 0.0, 2.01} - server, lastReq := CreateServer(prices...) - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345", - "secure": 1, - "tag_id": "top", - "position": 2, - "bidfloor": 1.01, - "mobile" : 1}` - - ctx := context.TODO() - pbReq, err := CreateBannerRequest(param, param, param) - if err != nil { - t.Fatal("Failed to create a banner request", err) - } - - resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - prices, imps := FilterZeroPrices(prices, lastReq.Imp) - - assertEqual(t, len(resp), len(prices), "Bad number of responses") - - for i, bid := range resp { - assertEqual(t, bid.Price, prices[i], "Bad price in response") - assertEqual(t, bid.AdUnitCode, imps[i].ID, "Bad bid id in response") - - if bid.Price > 0 { - assertEqual(t, bid.Adm, ExpectedAdM, "Bad ad markup in response") - assertEqual(t, bid.NURL, ExpectedNURL, "Bad notification url in response") - assertEqual(t, bid.Creative_id, ExpectedCrID, "Bad creative id in response") - assertEqual(t, bid.Width, *imps[i].Banner.W, "Bad width in response") - assertEqual(t, bid.Height, *imps[i].Banner.H, "Bad height in response") - } - } -} - -// Test video request - -func TestConversantBasicVideoRequest(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345", - "tag_id": "bottom left", - "position": 3, - "bidfloor": 1.01 }` - - ctx := context.TODO() - pbReq, err := CreateVideoRequest(param) - if err != nil { - t.Fatal("Failed to create a video request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") - imp := &lastReq.Imp[0] - - assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") - assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") - assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") - assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") - assertTrue(t, imp.Banner == nil, "Request banner should be nil") - assertEqual(t, int(*imp.Secure), 0, "Request secure") - assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") - assertEqual(t, imp.TagID, "bottom left", "Request tag id") - assertEqual(t, int(*imp.Video.Pos), 3, "Request pos") - assertEqual(t, int(imp.Video.W), 300, "Request width") - assertEqual(t, int(imp.Video.H), 250, "Request height") - - assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") - assertEqual(t, imp.Video.MIMEs[0], "video/mp4", "Requst video MIMEs type") - assertTrue(t, imp.Video.Protocols == nil, "Request video protocols") - assertEqual(t, imp.Video.MaxDuration, int64(0), "Request video 0 max duration") - assertTrue(t, imp.Video.API == nil, "Request video api should be nil") -} - -// Test video request with parameters in custom params object - -func TestConversantVideoRequestWithParams(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345", - "tag_id": "bottom left", - "position": 3, - "bidfloor": 1.01, - "mimes": ["video/x-ms-wmv"], - "protocols": [1, 2], - "api": [1, 2], - "maxduration": 90 }` - - ctx := context.TODO() - pbReq, err := CreateVideoRequest(param) - if err != nil { - t.Fatal("Failed to create a video request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") - imp := &lastReq.Imp[0] - - assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") - assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") - assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") - assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") - assertTrue(t, imp.Banner == nil, "Request banner should be nil") - assertEqual(t, int(*imp.Secure), 0, "Request secure") - assertEqual(t, imp.BidFloor, 1.01, "Request bid floor") - assertEqual(t, imp.TagID, "bottom left", "Request tag id") - assertEqual(t, int(*imp.Video.Pos), 3, "Request pos") - assertEqual(t, int(imp.Video.W), 300, "Request width") - assertEqual(t, int(imp.Video.H), 250, "Request height") - - assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") - assertEqual(t, imp.Video.MIMEs[0], "video/x-ms-wmv", "Requst video MIMEs type") - assertEqual(t, len(imp.Video.Protocols), 2, "Request video protocols") - assertEqual(t, imp.Video.Protocols[0], openrtb.Protocol(1), "Request video protocols 1") - assertEqual(t, imp.Video.Protocols[1], openrtb.Protocol(2), "Request video protocols 2") - assertEqual(t, imp.Video.MaxDuration, int64(90), "Request video 0 max duration") - assertEqual(t, len(imp.Video.API), 2, "Request video api should be nil") - assertEqual(t, imp.Video.API[0], openrtb.APIFramework(1), "Request video api 1") - assertEqual(t, imp.Video.API[1], openrtb.APIFramework(2), "Request video api 2") -} - -// Test video request with parameters in the video object - -func TestConversantVideoRequestWithParams2(t *testing.T) { - server, lastReq := CreateServer() - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345" }` - videoParam := `{ "mimes": ["video/x-ms-wmv"], - "protocols": [1, 2], - "maxduration": 90 }` - - ctx := context.TODO() - pbReq := CreateRequest(param) - pbReq, err := ConvertToVideoRequest(pbReq, videoParam) - if err != nil { - t.Fatal("Failed to convert to a video request", err) - } - pbReq, err = ParseRequest(pbReq) - if err != nil { - t.Fatal("Failed to parse video request", err) - } - - _, err = an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - assertEqual(t, len(lastReq.Imp), 1, "Request number of impressions") - imp := &lastReq.Imp[0] - - assertEqual(t, imp.DisplayManager, ExpectedDisplayManager, "Request display manager value") - assertEqual(t, lastReq.Site.ID, ExpectedSiteID, "Request site id") - assertEqual(t, int(lastReq.Site.Mobile), 0, "Request site mobile flag") - assertEqual(t, lastReq.User.BuyerUID, ExpectedBuyerUID, "Request buyeruid") - assertTrue(t, imp.Banner == nil, "Request banner should be nil") - assertEqual(t, int(*imp.Secure), 0, "Request secure") - assertEqual(t, imp.BidFloor, 0.0, "Request bid floor") - assertEqual(t, int(imp.Video.W), 300, "Request width") - assertEqual(t, int(imp.Video.H), 250, "Request height") - - assertEqual(t, len(imp.Video.MIMEs), 1, "Request video MIMEs entries") - assertEqual(t, imp.Video.MIMEs[0], "video/x-ms-wmv", "Requst video MIMEs type") - assertEqual(t, len(imp.Video.Protocols), 2, "Request video protocols") - assertEqual(t, imp.Video.Protocols[0], openrtb.Protocol(1), "Request video protocols 1") - assertEqual(t, imp.Video.Protocols[1], openrtb.Protocol(2), "Request video protocols 2") - assertEqual(t, imp.Video.MaxDuration, int64(90), "Request video 0 max duration") -} - -// Test video responses - -func TestConversantVideoResponse(t *testing.T) { - prices := []float64{0.01, 0.0, 2.01} - server, lastReq := CreateServer(prices...) - if server == nil { - t.Fatal("server not created") - } - - defer server.Close() - - // Create a adapter to test - - conf := *adapters.DefaultHTTPAdapterConfig - an := NewConversantAdapter(&conf, server.URL) - - param := `{ "site_id": "12345", - "secure": 1, - "tag_id": "top", - "position": 2, - "bidfloor": 1.01, - "mobile" : 1}` - - ctx := context.TODO() - pbReq, err := CreateVideoRequest(param, param, param) - if err != nil { - t.Fatal("Failed to create a video request", err) - } - - resp, err := an.Call(ctx, pbReq, pbReq.Bidders[0]) - if err != nil { - t.Fatal("Failed to retrieve bids", err) - } - - prices, imps := FilterZeroPrices(prices, lastReq.Imp) - - assertEqual(t, len(resp), len(prices), "Bad number of responses") - - for i, bid := range resp { - assertEqual(t, bid.Price, prices[i], "Bad price in response") - assertEqual(t, bid.AdUnitCode, imps[i].ID, "Bad bid id in response") - - if bid.Price > 0 { - assertEqual(t, bid.Adm, "", "Bad ad markup in response") - assertEqual(t, bid.NURL, ExpectedAdM, "Bad notification url in response") - assertEqual(t, bid.Creative_id, ExpectedCrID, "Bad creative id in response") - assertEqual(t, bid.Width, imps[i].Video.W, "Bad width in response") - assertEqual(t, bid.Height, imps[i].Video.H, "Bad height in response") - } - } -} - -// Helpers to create a banner and video requests - -func CreateRequest(params ...string) *pbs.PBSRequest { - num := len(params) - - req := pbs.PBSRequest{ - Tid: "t-000", - AccountID: "1", - AdUnits: make([]pbs.AdUnit, num), - } - - for i := 0; i < num; i++ { - req.AdUnits[i] = pbs.AdUnit{ - Code: fmt.Sprintf("au-%03d", i), - Sizes: []openrtb.Format{ - { - W: 300, - H: 250, - }, - }, - Bids: []pbs.Bids{ - { - BidderCode: "conversant", - BidID: fmt.Sprintf("b-%03d", i), - Params: json.RawMessage(params[i]), - }, - }, - } - } - - return &req -} - -// Convert a request to a video request by adding required properties - -func ConvertToVideoRequest(req *pbs.PBSRequest, videoParams ...string) (*pbs.PBSRequest, error) { - for i := 0; i < len(req.AdUnits); i++ { - video := pbs.PBSVideo{} - if i < len(videoParams) { - err := json.Unmarshal([]byte(videoParams[i]), &video) - if err != nil { - return nil, err - } - } - - if video.Mimes == nil { - video.Mimes = []string{"video/mp4"} - } - - req.AdUnits[i].Video = video - req.AdUnits[i].MediaTypes = []string{"video"} - } - - return req, nil -} - -// Convert a request to an app request by adding required properties -func ConvertToAppRequest(req *pbs.PBSRequest, appParams string) (*pbs.PBSRequest, error) { - app := new(openrtb.App) - err := json.Unmarshal([]byte(appParams), &app) - if err == nil { - req.App = app - } - - return req, nil -} - -// Feed the request thru the prebid parser so user id and -// other private properties are defined - -func ParseRequest(req *pbs.PBSRequest) (*pbs.PBSRequest, error) { - body := new(bytes.Buffer) - _ = json.NewEncoder(body).Encode(req) - - // Need to pass the conversant user id thru uid cookie - - httpReq := httptest.NewRequest("POST", "/foo", body) - cookie := usersync.NewPBSCookie() - _ = cookie.TrySync("conversant", ExpectedBuyerUID) - httpReq.Header.Set("Cookie", cookie.ToHTTPCookie(90*24*time.Hour).String()) - httpReq.Header.Add("Referer", "http://example.com") - cache, _ := dummycache.New() - hcc := config.HostCookie{} - - parsedReq, err := pbs.ParsePBSRequest(httpReq, &config.AuctionTimeouts{ - Default: 2000, - Max: 2000, - }, cache, &hcc) - - return parsedReq, err -} - -// A helper to create a banner request - -func CreateBannerRequest(params ...string) (*pbs.PBSRequest, error) { - req := CreateRequest(params...) - req, err := ParseRequest(req) - return req, err -} - -// A helper to create a video request - -func CreateVideoRequest(params ...string) (*pbs.PBSRequest, error) { - req := CreateRequest(params...) - req, err := ConvertToVideoRequest(req) - if err != nil { - return nil, err - } - req, err = ParseRequest(req) - return req, err -} - -// Helper to create a test http server that receives and generate openrtb requests and responses - -func CreateServer(prices ...float64) (*httptest.Server, *openrtb.BidRequest) { - var lastBidRequest openrtb.BidRequest - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - var bidReq openrtb.BidRequest - var price float64 - var bids []openrtb.Bid - var bid openrtb.Bid - - err = json.Unmarshal(body, &bidReq) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - - lastBidRequest = bidReq - - for i, imp := range bidReq.Imp { - if i < len(prices) { - price = prices[i] - } else { - price = 0 - } - - if price > 0 { - bid = openrtb.Bid{ - ID: imp.ID, - ImpID: imp.ID, - Price: price, - NURL: ExpectedNURL, - AdM: ExpectedAdM, - CrID: ExpectedCrID, - } - - if imp.Banner != nil { - bid.W = *imp.Banner.W - bid.H = *imp.Banner.H - } else if imp.Video != nil { - bid.W = imp.Video.W - bid.H = imp.Video.H - } - } else { - bid = openrtb.Bid{ - ID: imp.ID, - ImpID: imp.ID, - Price: 0, - } - } - - bids = append(bids, bid) - } - - if len(bids) == 0 { - w.WriteHeader(http.StatusNoContent) - } else { - js, _ := json.Marshal(openrtb.BidResponse{ - ID: bidReq.ID, - SeatBid: []openrtb.SeatBid{ - { - Bid: bids, - }, - }, - }) - - w.Header().Set("Content-Type", "application/json") - _, _ = w.Write(js) - } - }), - ) - - return server, &lastBidRequest -} - -// Helper to remove impressions with $0 bids - -func FilterZeroPrices(prices []float64, imps []openrtb.Imp) ([]float64, []openrtb.Imp) { - prices2 := make([]float64, 0) - imps2 := make([]openrtb.Imp, 0) - - for i := range prices { - if prices[i] > 0 { - prices2 = append(prices2, prices[i]) - imps2 = append(imps2, imps[i]) - } - } - - return prices2, imps2 -} - -// Helpers to test equality - -func assertEqual(t *testing.T, actual interface{}, expected interface{}, msg string) { - if expected != actual { - msg = fmt.Sprintf("%s: act(%v) != exp(%v)", msg, actual, expected) - t.Fatal(msg) - } -} - -func assertNotEqual(t *testing.T, actual interface{}, expected interface{}, msg string) { - if expected == actual { - msg = fmt.Sprintf("%s: act(%v) == exp(%v)", msg, actual, expected) - t.Fatal(msg) - } -} - -func assertTrue(t *testing.T, val bool, msg string) { - if val == false { - msg = fmt.Sprintf("%s: is false but should be true", msg) - t.Fatal(msg) - } -} - -func assertFalse(t *testing.T, val bool, msg string) { - if val == true { - msg = fmt.Sprintf("%s: is true but should be false", msg) - t.Fatal(msg) - } +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "conversanttest", NewConversantBidder("")) } diff --git a/adapters/conversant/conversanttest/exemplary/banner.json b/adapters/conversant/conversanttest/exemplary/banner.json new file mode 100644 index 00000000000..472e18f712d --- /dev/null +++ b/adapters/conversant/conversanttest/exemplary/banner.json @@ -0,0 +1,113 @@ +{ + "mockBidRequest": { + "id": "testauction", + "imp": [ + { + "id": "1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "site_id": "108060", + "bidfloor": 0.01, + "tag_id": "mytag", + "secure": 1 + } + } + } + ], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "192.168.1.1", + "dnt": 1 + }, + "site": { + "domain": "www.mypage.com" + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "", + "body": { + "id": "testauction", + "site": { + "id": "108060", + "domain": "www.mypage.com" + }, + "imp": [ + { + "id": "1", + "tagid": "mytag", + "secure": 1, + "bidfloor": 0.01, + "displaymanager": "prebid-s2s", + "displaymanagerver": "2.0.0", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "site_id": "108060", + "bidfloor": 0.01, + "tag_id": "mytag", + "secure": 1 + } + } + } + ], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "192.168.1.1", + "dnt": 1 + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "testauction", + "bidid": "c8d95f4b-bcbb-4a6c-adbb-4c7f33af3c24", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "1", + "impid": "1", + "price": 0.0340, + "nurl": "https:\/\/event.ad.cpe.dotomi.com\/cvx\/event\/imp?enc=eyJ1c2VyaWQiOiI3MTI3MDUzNzM3NTM3MTAzMjIiLCJwYXJ0bmVyVHhpZCI6ImUyZWUzNjZlLWEyMjgtNDI0Mi1hNjJlLTk4ODk3ODhiYzgxNCIsInR4aWQiOiI3MTE1NzQwNDg3NTczODUwMDIiLCJuZXR3b3JrUmVxdWVzdElkIjoiNzExNTc0MDQ4NzU3Mzg1ODc0Iiwic2lkIjoxMTgwOTgsImRpdmlzaW9uSWQiOjgsInRpZCI6OCwibW9iaWxlRGF0YSI6IjU5IiwiYmlkUHJpY2UiOjAuMDY4MCwicHViQ29zdCI6MC4wMzQwLCJwYXJ0bmVyRmVlIjowLjAxMzYsImlwU3RyaW5nIjoiNzMuMTE4LjEzMC4xODYiLCJzdXBwbHlUeXBlIjoxLCJpbnRlZ3JhdGlvblR5cGUiOjQsIm1lZGlhdGlvblR5cGUiOjEyNiwicGxhY2VtZW50SWQiOiIxMTY5ODcwIiwiaGVhZGVyQmlkIjoxLCJpc0RpcmVjdFB1Ymxpc2hlciI6MCwiaGFzQ29uc2VudCI6MSwib3BlcmF0aW9uIjoiQ0xJRU5UX0hFQURFUl8yNSIsImlzQ29yZVNoaWVsZCI6MCwicGFydG5lckNyZWF0aXZlSWQiOiIyNDk2NDRfMzAweDI1MCIsInBhcnRuZXJEb21haW5zIjpbIndhbG1hcnQuY29tIl0sInNlbGxlclJlcXVlc3RJZCI6ImE3ODcyMWQ3LWE2ZmUtNGJiNS1hNjFkLTFhMDg1MzkxZTVlZCIsInNlbGxlckltcElkIjoiMzAwNDIxZDY0NWY2ZjRjOWMifQ&", + "adm": "", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "ttl": 300, + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }] + }, + { + "seat": "45678", + "bid": [{ + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "ttl": 300, + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + } + ] + }], + "cur": "USD" + } + } + }], + + "expectedBids": [ + { + "bid": { + "adm": "
", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "ttl": 300, + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "ttl": 300, + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + }, + "type": "video" + } + ] +} + \ No newline at end of file diff --git a/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json b/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json new file mode 100644 index 00000000000..c2b20cf1c5d --- /dev/null +++ b/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json @@ -0,0 +1,200 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id_1", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + } + }, + { + "id": "some_test_ad_id_2", + "video":{ + "mimes": [ + "video/mp4", + "application/javascript" + ], + "protocols":[ + 2, + 3, + 5, + 6 + ], + "w":640, + "h":480 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + } + } + ], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://hb.emxdgt.com?t=1000&ts=2060541160", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Referer": [ + "http://www.publisher.com/awesome/site?with=some¶meters=here" + ], + "Dnt": [ + "1" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36" + ] + }, + "body": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id_1", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + }, + "tagid": "25251", + "secure": 0 + }, + { + "id": "some_test_ad_id_2", + "video":{ + "mimes": [ + "video/mp4", + "application/javascript" + ], + "protocols":[ + 2, + 3, + 5, + 6 + ], + "w":640, + "h":480 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + }, + "tagid": "25251", + "secure": 0 + }], + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/awesome/site?with=some¶meters=here" + }, + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [{ + "seat": "12356", + "bid": [{ + "adm": "
", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "ttl": 300, + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }] + }, + { + "seat": "45678", + "bid": [{ + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "ttl": 300, + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + } + ] + }], + "cur": "USD" + } + } + }], + + "expectedBids": [{ + "bid": { + "adm": "
", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "ttl": 300, + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "ttl": 300, + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + }, + "type": "video" + } + ] +} + \ No newline at end of file diff --git a/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json b/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json new file mode 100644 index 00000000000..8de90f52192 --- /dev/null +++ b/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + } + }], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + }, + "app": { + "domain": "www.publisher.com", + "storeurl": "http://www.publisher.com/awesome/site?with=some¶meters=here" + } + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://hb.emxdgt.com?t=1000&ts=2060541160", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Dnt": [ + "1" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36" + ] + }, + "body": { + "id": "some_test_auction", + "imp": [{ + "id": "some_test_ad_id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "tagid": "25251" + } + }, + "tagid": "25251", + "secure": 0 + }], + "app": { + "domain": "www.publisher.com", + "storeurl": "http://www.publisher.com/awesome/site?with=some¶meters=here" + }, + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123", + "dnt": 1 + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "some_test_auction", + "seatbid": [{ + "seat": "12356", + "bid": [{ + "adm": "
", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "h": 300, + "w": 250 + }] + }], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "TEST", + "impid": "1", + "price": 10.0, + "adid": "1", + "adm": "", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "h": 300, + "w": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/amx/amxtest/exemplary/video-simple.json b/adapters/amx/amxtest/exemplary/video-simple.json new file mode 100644 index 00000000000..8fb3baa26d0 --- /dev/null +++ b/adapters/amx/amxtest/exemplary/video-simple.json @@ -0,0 +1,245 @@ +{ + "mockBidRequest": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "boxingallowed": 1, + "linearity": 1, + "maxduration": 90, + "minduration": 6, + "mimes": ["video/mp4"], + "placement": 1, + "playbackmethod": [2], + "protocols": [1,2,3,4,5,6,7,8], + "skip": 1, + "skipafter": 5, + "startdelay": 0, + "h": 300, + "pos": 1, + "w": 640 + }, + "ext": { + "bidder": { + "tagId": "cHJlYmlkLm9yZw", + "adUnitId": "tagid-override" + } + }, + "id": "1", + "secure": 1 + } + ], + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "1---" + } + }, + "site": { + "domain": "www.example.com", + "ext": { + "amp": 0 + }, + "publisher": { + "id": "unused_publisher_id" + }, + "page": "https://www.example.com/es6/es6_objects.htm", + "ref": "https://www.example.com/es6/es6_objects.htm" + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "test": 0, + "tmax": 300, + "user": { + "ext": { + "eids": [ + { + "source": "amxid", + "uids": [ + { + "atype": 1, + "id": "88de601e-3d98-48e7-81d7-00000000" + } + ] + } + ], + "gdpr": 0, + "us_privacy": "1---" + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "body": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "boxingallowed": 1, + "linearity": 1, + "maxduration": 90, + "minduration": 6, + "mimes": ["video/mp4"], + "placement": 1, + "playbackmethod": [2], + "protocols": [1,2,3,4,5,6,7,8], + "skip": 1, + "skipafter": 5, + "startdelay": 0, + "h": 300, + "pos": 1, + "w": 640 + }, + "ext": { + "bidder": { + "tagId": "cHJlYmlkLm9yZw", + "adUnitId": "tagid-override" + } + }, + "tagid": "tagid-override", + "id": "1", + "secure": 1 + } + ], + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "1---" + } + }, + "site": { + "domain": "www.example.com", + "ext": { + "amp": 0 + }, + "publisher": { + "id": "cHJlYmlkLm9yZw" + }, + "page": "https://www.example.com/es6/es6_objects.htm", + "ref": "https://www.example.com/es6/es6_objects.htm" + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "tmax": 300, + "user": { + "ext": { + "eids": [ + { + "source": "amxid", + "uids": [ + { + "atype": 1, + "id": "88de601e-3d98-48e7-81d7-00000000" + } + ] + } + ], + "gdpr": 0, + "us_privacy": "1---" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "WQ5V2DWVTMNXABDD", + "seatbid": [{ + "bid": [{ + "id": "TEST", + "impid": "1", + "price": 10.0, + "adid": "1", + "adm": "00:00:15", + "nurl": "https://example.com/nurl", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "h": 600, + "w": 300, + "ext": { + "himp": ["https://example.com/imp-tracker/pixel.gif?param=1¶m2=2"], + "startdelay": 0 + } + }] + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "TEST", + "impid": "1", + "price": 10.0, + "adid": "1", + "adm": "00:00:15", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "ext": { + "himp": ["https://example.com/imp-tracker/pixel.gif?param=1¶m2=2"], + "startdelay": 0 + }, + "h": 600, + "w": 300 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/amx/amxtest/exemplary/web-simple.json b/adapters/amx/amxtest/exemplary/web-simple.json new file mode 100644 index 00000000000..74854f912ae --- /dev/null +++ b/adapters/amx/amxtest/exemplary/web-simple.json @@ -0,0 +1,246 @@ +{ + "mockBidRequest": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "banner": { + "format": [ + { + "h": 600, + "w": 300 + } + ], + "h": 600, + "pos": 1, + "w": 300 + }, + "ext": { + "bidder": { + "tagId": "cHJlYmlkLm9yZw" + } + }, + "tagid": "example-tag-id", + "id": "1", + "secure": 1 + } + ], + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "1---" + } + }, + "site": { + "domain": "www.example.com", + "ext": { + "amp": 0 + }, + "publisher": { + "id": "unused_publisher_id" + }, + "page": "https://www.example.com/es6/es6_objects.htm", + "ref": "https://www.example.com/es6/es6_objects.htm" + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "test": 0, + "tmax": 300, + "user": { + "ext": { + "eids": [ + { + "source": "amxid", + "uids": [ + { + "atype": 1, + "id": "88de601e-3d98-48e7-81d7-00000000" + } + ] + }, + { + "source": "adserver.org", + "uids": [ + { + "id": "1234567", + "ext": { + "rtiPartner": "TDID" + } + } + ] + } + ], + "gdpr": 0, + "us_privacy": "1---" + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "body": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "tagid": "example-tag-id", + "banner": { + "format": [ + { + "h": 600, + "w": 300 + } + ], + "h": 600, + "pos": 1, + "w": 300 + }, + "ext": { + "bidder": { + "tagId": "cHJlYmlkLm9yZw" + } + }, + "id": "1", + "secure": 1 + } + ], + "regs": { + "ext": { + "gdpr": 0, + "us_privacy": "1---" + } + }, + "site": { + "domain": "www.example.com", + "ext": { + "amp": 0 + }, + "publisher": { + "id": "cHJlYmlkLm9yZw" + }, + "page": "https://www.example.com/es6/es6_objects.htm", + "ref": "https://www.example.com/es6/es6_objects.htm" + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "tmax": 300, + "user": { + "ext": { + "eids": [ + { + "source": "amxid", + "uids": [ + { + "atype": 1, + "id": "88de601e-3d98-48e7-81d7-00000000" + } + ] + }, + { + "source": "adserver.org", + "uids": [ + { + "id": "1234567", + "ext": { + "rtiPartner": "TDID" + } + } + ] + } + ], + "gdpr": 0, + "us_privacy": "1---" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "WQ5V2DWVTMNXABDD", + "seatbid": [{ + "bid": [{ + "id": "TEST", + "impid": "1", + "price": 10.0, + "adid": "1", + "adm": "", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "h": 600, + "w": 300 + }] + }], + "cur": "USD" + } + } + }], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "TEST", + "impid": "1", + "price": 10.0, + "adid": "1", + "adm": "", + "adomain": ["amxrtb.com"], + "iurl": "https://assets.a-mo.net/300x250.v2.png", + "cid": "1", + "crid": "1", + "h": 600, + "w": 300 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/amx/amxtest/params/race/display.json b/adapters/amx/amxtest/params/race/display.json new file mode 100644 index 00000000000..bd101e95a25 --- /dev/null +++ b/adapters/amx/amxtest/params/race/display.json @@ -0,0 +1 @@ +{"tagId":"sample345", "adUnitId": "sampleAdUnitID"} \ No newline at end of file diff --git a/adapters/amx/amxtest/params/race/video.json b/adapters/amx/amxtest/params/race/video.json new file mode 100644 index 00000000000..d2f11bf80b4 --- /dev/null +++ b/adapters/amx/amxtest/params/race/video.json @@ -0,0 +1 @@ +{"tagId": "sample123", "adUnitId": "sampleAdUnitID"} \ No newline at end of file diff --git a/adapters/amx/amxtest/supplemental/204-response.json b/adapters/amx/amxtest/supplemental/204-response.json new file mode 100644 index 00000000000..09571a03569 --- /dev/null +++ b/adapters/amx/amxtest/supplemental/204-response.json @@ -0,0 +1,109 @@ +{ + "mockBidRequest": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "test": 0, + "tmax": 300 + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "body": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "mimes": null, + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "tmax": 300 + } + }, + "mockResponse": { + "status": 204, + "headers": { + "X-Nbr": [ + "3b" + ] + }, + "body": {} + } + }], + "expectedMakeBidsErrors": [] +} diff --git a/adapters/amx/amxtest/supplemental/400-response.json b/adapters/amx/amxtest/supplemental/400-response.json new file mode 100644 index 00000000000..f10cea89718 --- /dev/null +++ b/adapters/amx/amxtest/supplemental/400-response.json @@ -0,0 +1,114 @@ +{ + "mockBidRequest": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "test": 0, + "tmax": 300 + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "body": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "mimes": null, + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "tmax": 300 + } + }, + "mockResponse": { + "status": 400, + "headers": { + "X-Nbr": [ + "3b" + ] + }, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Invalid Request: 400. Error Code: 3b", + "comparison": "literal" + } + ] +} diff --git a/adapters/amx/amxtest/supplemental/500-response.json b/adapters/amx/amxtest/supplemental/500-response.json new file mode 100644 index 00000000000..fe5d89930c8 --- /dev/null +++ b/adapters/amx/amxtest/supplemental/500-response.json @@ -0,0 +1,114 @@ +{ + "mockBidRequest": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "test": 0, + "tmax": 300 + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "body": { + "device": { + "dnt": 0, + "h": 1120, + "ip": "98.249.0.0", + "language": "en", + "os": "macos", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36", + "w": 1792 + }, + "id": "TL3JS6F43CKNDQFY", + "imp": [ + { + "video": { + "api": [1,2], + "mimes": null, + "h": 300, + "pos": 1, + "w": 640 + }, + "id": "1", + "secure": 1 + } + ], + "site": { + "ext": { + "amp": 0 + } + }, + "source": { + "ext": { + "schain": { + "complete": 1, + "nodes": [ + { + "asi": "amxrtb.com", + "hp": 1, + "sid": "1234" + } + ], + "ver": "1.0" + } + } + }, + "tmax": 300 + } + }, + "mockResponse": { + "status": 500, + "headers": { + "X-Nbr": [ + "7a" + ] + }, + "body": {} + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected response: 500. Error Code: 7a", + "comparison": "literal" + } + ] +} diff --git a/adapters/amx/params_test.go b/adapters/amx/params_test.go new file mode 100644 index 00000000000..89e9a3adeb4 --- /dev/null +++ b/adapters/amx/params_test.go @@ -0,0 +1,47 @@ +package amx + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +var validBidParams = []string{ + `{"tagId":"sampleTagId", "adUnitId": "sampleAdUnitId"}`, + `{"tagId":"sampleTagId", "adUnitId": ""}`, + `{"adUnitId": ""}`, + `{"adUnitId": "sampleAdUnitId"}`, + `{"tagId":"sampleTagId"}`, + `{"tagId":""}`, + `{}`, + `{"otherValue": "ignored"}`, + `{"tagId": "sampleTagId", "otherValue": "ignored"}`, + `{"otherValue": "ignored", "adUnitId": "sampleAdUnitId"}`, +} + +var invalidBidParams = []string{ + `{"tagId":1234}`, + `{"tagId": true}`, + `{"adUnitId": true}`, + `{"adUnitId": null}`, + `{"adUnitId": null, "tagId": "sampleTagId"}`, + `{"adUnitId": 1234, "tagId": "sampleTagId"}`, +} + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + assert.Nil(t, err) + for _, params := range validBidParams { + assert.Nil(t, validator.Validate(openrtb_ext.BidderAMX, json.RawMessage(params))) + } +} + +func TestInValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + assert.Nil(t, err) + for _, params := range invalidBidParams { + assert.NotNil(t, validator.Validate(openrtb_ext.BidderAMX, json.RawMessage(params))) + } +} diff --git a/adapters/amx/usersync.go b/adapters/amx/usersync.go new file mode 100644 index 00000000000..28e6ac0ed79 --- /dev/null +++ b/adapters/amx/usersync.go @@ -0,0 +1,13 @@ +package amx + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +// NewAMXSyncer produces an AMX RTB usersyncer +func NewAMXSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("amx", 737, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/amx/usersync_test.go b/adapters/amx/usersync_test.go new file mode 100644 index 00000000000..20a47c33b69 --- /dev/null +++ b/adapters/amx/usersync_test.go @@ -0,0 +1,23 @@ +package amx + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/stretchr/testify/assert" +) + +func TestAMXSyncer(t *testing.T) { + syncURL := "http://pbs.amxrtb.com/cchain/0?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&cb=localhost%2Fsetuid%3Fbidder%3Damx%26uid%3D" + syncURLTemplate := template.Must(template.New("sync-template").Parse(syncURL)) + + syncer := NewAMXSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{}) + + assert.NoError(t, err) + assert.Equal(t, "http://pbs.amxrtb.com/cchain/0?gdpr=&gdpr_consent=&cb=localhost%2Fsetuid%3Fbidder%3Damx%26uid%3D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 737, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go index de33bd390e5..dfb97f33abb 100644 --- a/adapters/dmx/dmx.go +++ b/adapters/dmx/dmx.go @@ -4,13 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "net/http" + "net/url" + "strings" + "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" - "net/http" - "net/url" - "strings" ) type DmxAdapter struct { diff --git a/config/config.go b/config/config.go index 6d8fdbc070d..c2db7b7d03c 100755 --- a/config/config.go +++ b/config/config.go @@ -712,6 +712,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderAdOcean doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAJA, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Daja%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25s") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAMX, "https://prebid.a-mo.net/cchain/0?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Damx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAvocet, "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") @@ -939,6 +940,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.adtelligent.endpoint", "http://ghb.adtelligent.com/pbs/ortb") v.SetDefault("adapters.advangelists.endpoint", "http://nep.advangelists.com/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.aja.endpoint", "https://ad.as.amanad.adtdp.com/v1/bid/4") + v.SetDefault("adapters.amx.endpoint", "http://pbs.amxrtb.com/auction/openrtb") v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid") v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs v.SetDefault("adapters.appnexus.platform_id", "5") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 30d8d45641f..ff72165bcb3 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -25,6 +25,7 @@ import ( "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" "github.com/prebid/prebid-server/adapters/aja" + "github.com/prebid/prebid-server/adapters/amx" "github.com/prebid/prebid-server/adapters/applogy" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" @@ -122,6 +123,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAdtelligent: adtelligent.NewAdtelligentBidder(cfg.Adapters[string(openrtb_ext.BidderAdtelligent)].Endpoint), openrtb_ext.BidderAdvangelists: advangelists.NewAdvangelistsBidder(cfg.Adapters[string(openrtb_ext.BidderAdvangelists)].Endpoint), openrtb_ext.BidderAJA: aja.NewAJABidder(cfg.Adapters[string(openrtb_ext.BidderAJA)].Endpoint), + openrtb_ext.BidderAMX: amx.NewAMXBidder(cfg.Adapters[string(openrtb_ext.BidderAMX)].Endpoint), openrtb_ext.BidderApplogy: applogy.NewApplogyBidder(cfg.Adapters[string(openrtb_ext.BidderApplogy)].Endpoint), openrtb_ext.BidderAppnexus: appnexus.NewAppNexusBidder(client, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].PlatformID), openrtb_ext.BidderAvocet: avocet.NewAvocetAdapter(cfg.Adapters[string(openrtb_ext.BidderAvocet)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index dd482a2ea44..b98c80ae9af 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -43,6 +43,7 @@ const ( BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" BidderAJA BidderName = "aja" + BidderAMX BidderName = "amx" BidderApplogy BidderName = "applogy" BidderAppnexus BidderName = "appnexus" BidderAdoppler BidderName = "adoppler" @@ -136,6 +137,7 @@ var BidderMap = map[string]BidderName{ "adtelligent": BidderAdtelligent, "advangelists": BidderAdvangelists, "aja": BidderAJA, + "amx": BidderAMX, "applogy": BidderApplogy, "appnexus": BidderAppnexus, "adoppler": BidderAdoppler, diff --git a/openrtb_ext/imp_amx.go b/openrtb_ext/imp_amx.go new file mode 100644 index 00000000000..d4439d05f60 --- /dev/null +++ b/openrtb_ext/imp_amx.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpAMX is the imp.ext format for the AMX bidder +type ExtImpAMX struct { + TagID string `json:"tagId,omitempty"` + AdUnitID string `json:"adUnitId,omitempty"` +} diff --git a/static/bidder-info/amx.yaml b/static/bidder-info/amx.yaml new file mode 100644 index 00000000000..3e20d2095f6 --- /dev/null +++ b/static/bidder-info/amx.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "prebid@amxrtb.com" +capabilities: + site: + mediaTypes: + - banner + - video + app: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-params/amx.json b/static/bidder-params/amx.json new file mode 100644 index 00000000000..f9b1b26b3db --- /dev/null +++ b/static/bidder-params/amx.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AMX RTB Adapter Params", + "description": "A schema to validate params accepted by the AMX adapter", + "type": "object", + "properties": { + "tagId" : { + "type": "string", + "description": "Set a tagId (overrides site.publisher.id, or app.publisher.id)" + }, + "adUnitId": { + "type": "string", + "description": "Override imp.tagid value to provide a custom value in AMX ad unit ID reporting" + } + } +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index a9d909db9a1..d6b3092a56d 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/adapters/adtelligent" "github.com/prebid/prebid-server/adapters/advangelists" "github.com/prebid/prebid-server/adapters/aja" + "github.com/prebid/prebid-server/adapters/amx" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" "github.com/prebid/prebid-server/adapters/avocet" @@ -102,6 +103,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdtelligent, adtelligent.NewAdtelligentSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAJA, aja.NewAJASyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAMX, amx.NewAMXSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAppnexus, appnexus.NewAppnexusSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAvocet, avocet.NewAvocetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeachfront, beachfront.NewBeachfrontSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 8ae8581aa6e..7582055fa46 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -27,6 +27,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdtelligent): syncConfig, string(openrtb_ext.BidderAdvangelists): syncConfig, string(openrtb_ext.BidderAJA): syncConfig, + string(openrtb_ext.BidderAMX): syncConfig, string(openrtb_ext.BidderAppnexus): syncConfig, string(openrtb_ext.BidderAvocet): syncConfig, string(openrtb_ext.BidderBeachfront): syncConfig, From 63f5bcfb9f310f62299ecaac5ef053dee31041fc Mon Sep 17 00:00:00 2001 From: htang555 Date: Tue, 10 Nov 2020 13:37:04 -0500 Subject: [PATCH 246/318] update Datablocks usersync.go (#1572) --- adapters/datablocks/usersync.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/datablocks/usersync.go b/adapters/datablocks/usersync.go index c8ec92aa857..2b47b259e39 100644 --- a/adapters/datablocks/usersync.go +++ b/adapters/datablocks/usersync.go @@ -7,8 +7,8 @@ import ( "github.com/prebid/prebid-server/usersync" ) -const datablocksGDPRVendorID = uint16(14) +const datablocksGDPRVendorID = uint16(0) func NewDatablocksSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("datablocks", 14, temp, adapters.SyncTypeRedirect) + return adapters.NewSyncer("datablocks", datablocksGDPRVendorID, temp, adapters.SyncTypeRedirect) } From 6d37afcdc109a4671644a18a66181c1bd75e6568 Mon Sep 17 00:00:00 2001 From: Aparna Rao Date: Wed, 11 Nov 2020 03:59:50 -0500 Subject: [PATCH 247/318] 33Across: Add video support in adapter (#1557) --- adapters/33across/33across.go | 77 +++++++++++- adapters/33across/33across_test.go | 2 +- .../exemplary/bidresponse-defaults.json | 100 ++++++++++++++++ .../exemplary/instream-video-defaults.json | 108 +++++++++++++++++ .../33acrosstest/exemplary/multi-format.json | 103 ++++++++++++++++ .../exemplary/optional-params.json | 0 .../exemplary/outstream-video-defaults.json | 107 +++++++++++++++++ .../exemplary/simple-banner.json | 14 ++- .../33acrosstest/exemplary/simple-video.json | 110 ++++++++++++++++++ .../params/race/banner.json | 0 .../33acrosstest/params/race/video.json | 6 + .../supplemental/status-not-ok.json | 67 +++++++++++ .../supplemental/video-validation-fail.json | 32 +++++ static/bidder-info/33across.yaml | 2 + 14 files changed, 724 insertions(+), 4 deletions(-) create mode 100644 adapters/33across/33acrosstest/exemplary/bidresponse-defaults.json create mode 100644 adapters/33across/33acrosstest/exemplary/instream-video-defaults.json create mode 100644 adapters/33across/33acrosstest/exemplary/multi-format.json rename adapters/33across/{33across => 33acrosstest}/exemplary/optional-params.json (100%) create mode 100644 adapters/33across/33acrosstest/exemplary/outstream-video-defaults.json rename adapters/33across/{33across => 33acrosstest}/exemplary/simple-banner.json (85%) create mode 100644 adapters/33across/33acrosstest/exemplary/simple-video.json rename adapters/33across/{33across => 33acrosstest}/params/race/banner.json (100%) create mode 100644 adapters/33across/33acrosstest/params/race/video.json create mode 100644 adapters/33across/33acrosstest/supplemental/status-not-ok.json create mode 100644 adapters/33across/33acrosstest/supplemental/video-validation-fail.json diff --git a/adapters/33across/33across.go b/adapters/33across/33across.go index 5c1b31eeb8c..40099a204e0 100644 --- a/adapters/33across/33across.go +++ b/adapters/33across/33across.go @@ -24,6 +24,14 @@ type ext struct { Zoneid string `json:"zoneid,omitempty"` } +type bidExt struct { + Ttx bidTtxExt `json:"ttx,omitempty"` +} + +type bidTtxExt struct { + MediaType string `json:mediaType,omitempty` +} + // MakeRequests create the object for TTX Reqeust. func (a *TtxAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var errs []error @@ -49,6 +57,14 @@ func (a *TtxAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.Request errs = append(errs, err) } + if reqCopy.Imp[0].Banner == nil && reqCopy.Imp[0].Video == nil { + errs = append(errs, &errortypes.BadInput{ + Message: "At least one of [banner, video] formats must be defined in Imp. None found", + }) + + return nil, errs + } + // Last Step reqJSON, err := json.Marshal(reqCopy) if err != nil { @@ -104,6 +120,19 @@ func preprocess(request *openrtb.BidRequest) error { siteCopy.ID = ttxExt.SiteId request.Site = &siteCopy + // Validate Video if it exists + if imp.Video != nil { + videoCopy, err := validateVideoParams(imp.Video, impExt.Ttx.Prod) + + imp.Video = videoCopy + + if err != nil { + return &errortypes.BadInput{ + Message: err.Error(), + } + } + } + return nil } @@ -135,9 +164,18 @@ func (a *TtxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalReque for _, sb := range bidResp.SeatBid { for i := range sb.Bid { + var bidExt bidExt + var bidType openrtb_ext.BidType + + if err := json.Unmarshal(sb.Bid[i].Ext, &bidExt); err != nil { + bidType = openrtb_ext.BidTypeBanner + } else { + bidType = getBidType(bidExt) + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &sb.Bid[i], - BidType: "banner", + BidType: bidType, }) } } @@ -145,6 +183,43 @@ func (a *TtxAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalReque } +func validateVideoParams(video *openrtb.Video, prod string) (*openrtb.Video, error) { + videoCopy := video + if videoCopy.W == 0 || + videoCopy.H == 0 || + videoCopy.Protocols == nil || + videoCopy.MIMEs == nil || + videoCopy.PlaybackMethod == nil { + + return nil, &errortypes.BadInput{ + Message: "One or more invalid or missing video field(s) w, h, protocols, mimes, playbackmethod", + } + } + + if videoCopy.Placement == 0 { + videoCopy.Placement = 2 + } + + if prod == "instream" { + videoCopy.Placement = 1 + + if videoCopy.StartDelay == nil { + videoCopy.StartDelay = openrtb.StartDelay.Ptr(0) + } + } + + return videoCopy, nil +} + +func getBidType(ext bidExt) openrtb_ext.BidType { + if ext.Ttx.MediaType == "video" { + return openrtb_ext.BidTypeVideo + } + + return openrtb_ext.BidTypeBanner +} + +// New33AcrossBidder configures bidder endpoint func New33AcrossBidder(endpoint string) *TtxAdapter { return &TtxAdapter{ endpoint: endpoint, diff --git a/adapters/33across/33across_test.go b/adapters/33across/33across_test.go index ccdcbf9cc2c..9856f8f9f6a 100644 --- a/adapters/33across/33across_test.go +++ b/adapters/33across/33across_test.go @@ -7,5 +7,5 @@ import ( ) func TestJsonSamples(t *testing.T) { - adapterstest.RunJSONBidderTest(t, "33across", New33AcrossBidder("http://ssc.33across.com")) + adapterstest.RunJSONBidderTest(t, "33acrosstest", New33AcrossBidder("http://ssc.33across.com")) } diff --git a/adapters/33across/33acrosstest/exemplary/bidresponse-defaults.json b/adapters/33across/33acrosstest/exemplary/bidresponse-defaults.json new file mode 100644 index 00000000000..bb0e6585fd0 --- /dev/null +++ b/adapters/33across/33acrosstest/exemplary/bidresponse-defaults.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "instream" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "ttx": { + "prod": "instream" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/33across/33acrosstest/exemplary/instream-video-defaults.json b/adapters/33across/33acrosstest/exemplary/instream-video-defaults.json new file mode 100644 index 00000000000..479b197077a --- /dev/null +++ b/adapters/33across/33acrosstest/exemplary/instream-video-defaults.json @@ -0,0 +1,108 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "instream" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": 0, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "ttx": { + "prod": "instream" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/33across/33acrosstest/exemplary/multi-format.json b/adapters/33across/33acrosstest/exemplary/multi-format.json new file mode 100644 index 00000000000..db15955ca87 --- /dev/null +++ b/adapters/33across/33acrosstest/exemplary/multi-format.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "inview" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "ttx": { + "prod": "inview" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/33across/33across/exemplary/optional-params.json b/adapters/33across/33acrosstest/exemplary/optional-params.json similarity index 100% rename from adapters/33across/33across/exemplary/optional-params.json rename to adapters/33across/33acrosstest/exemplary/optional-params.json diff --git a/adapters/33across/33acrosstest/exemplary/outstream-video-defaults.json b/adapters/33across/33acrosstest/exemplary/outstream-video-defaults.json new file mode 100644 index 00000000000..c0c31168684 --- /dev/null +++ b/adapters/33across/33acrosstest/exemplary/outstream-video-defaults.json @@ -0,0 +1,107 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "siab" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "ttx": { + "prod": "siab" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/33across/33across/exemplary/simple-banner.json b/adapters/33across/33acrosstest/exemplary/simple-banner.json similarity index 85% rename from adapters/33across/33across/exemplary/simple-banner.json rename to adapters/33across/33acrosstest/exemplary/simple-banner.json index 074badade07..d8c215c06ae 100644 --- a/adapters/33across/33across/exemplary/simple-banner.json +++ b/adapters/33across/33acrosstest/exemplary/simple-banner.json @@ -56,7 +56,12 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 90, - "w": 728 + "w": 728, + "ext": { + "ttx": { + "mediaType": "banner" + } + } }] } ], @@ -78,7 +83,12 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 728, - "h": 90 + "h": 90, + "ext": { + "ttx": { + "mediaType": "banner" + } + } }, "type": "banner" } diff --git a/adapters/33across/33acrosstest/exemplary/simple-video.json b/adapters/33across/33acrosstest/exemplary/simple-video.json new file mode 100644 index 00000000000..55337b92827 --- /dev/null +++ b/adapters/33across/33acrosstest/exemplary/simple-video.json @@ -0,0 +1,110 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "instream" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "ttx": { + "prod": "instream" + } + } + } + ], + "site": { + "id": "fake-site-id" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "ttx": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/33across/33across/params/race/banner.json b/adapters/33across/33acrosstest/params/race/banner.json similarity index 100% rename from adapters/33across/33across/params/race/banner.json rename to adapters/33across/33acrosstest/params/race/banner.json diff --git a/adapters/33across/33acrosstest/params/race/video.json b/adapters/33across/33acrosstest/params/race/video.json new file mode 100644 index 00000000000..9df849ad94b --- /dev/null +++ b/adapters/33across/33acrosstest/params/race/video.json @@ -0,0 +1,6 @@ +{ + "productId": "siab", + "siteId": "33across", + "zoneId": "33AcrossZone" + } + \ No newline at end of file diff --git a/adapters/33across/33acrosstest/supplemental/status-not-ok.json b/adapters/33across/33acrosstest/supplemental/status-not-ok.json new file mode 100644 index 00000000000..98fe01c2e50 --- /dev/null +++ b/adapters/33across/33acrosstest/supplemental/status-not-ok.json @@ -0,0 +1,67 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "siteId": "fake-invalid-site-id", + "productId": "inview" + } + } + } + ], + "site": {} + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://ssc.33across.com", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "ttx": { + "prod": "inview" + } + } + } + ], + "site": { + "id": "fake-invalid-site-id" + } + } + }, + "mockResponse": { + "status": 400, + "body": { + "error": { + "message": "Validation failed", + "details": [ + { + "message": "site.id is invalid" + } + ] + } + } + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/33across/33acrosstest/supplemental/video-validation-fail.json b/adapters/33across/33acrosstest/supplemental/video-validation-fail.json new file mode 100644 index 00000000000..97cb79bd26c --- /dev/null +++ b/adapters/33across/33acrosstest/supplemental/video-validation-fail.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90 + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "siab" + } + } + } + ], + "site": {} + }, + + "expectedMakeRequestsErrors": [ + { + "value": "One or more invalid or missing video field(s) w, h, protocols, mimes, playbackmethod", + "comparison": "literal" + }, + { + "value": "At least one of [banner, video] formats must be defined in Imp. None found", + "comparison": "literal" + } + ] +} diff --git a/static/bidder-info/33across.yaml b/static/bidder-info/33across.yaml index 84ba6d68611..67e6996accf 100644 --- a/static/bidder-info/33across.yaml +++ b/static/bidder-info/33across.yaml @@ -4,6 +4,8 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner + - video From c481f56a9f3ca5b405a3d623e107f7389c34dc18 Mon Sep 17 00:00:00 2001 From: silvermob <73727464+silvermob@users.noreply.github.com> Date: Wed, 11 Nov 2020 21:25:30 +0700 Subject: [PATCH 248/318] SilverMob adapter (#1561) * SilverMob adapter * Fixes andchanges according to notes in PR * Remaining fixes: multibids, expectedMakeRequestsErrors * removed log * removed log * Multi-bid test * Removed unnesesary block Co-authored-by: Anton Nikityuk --- adapters/silvermob/params_test.go | 56 ++++ adapters/silvermob/silvermob.go | 188 +++++++++++ adapters/silvermob/silvermob_test.go | 11 + .../silvermobtest/exemplary/banner-app.json | 163 ++++++++++ .../exemplary/banner-multi-app.json | 293 ++++++++++++++++++ .../silvermobtest/exemplary/native-app.json | 159 ++++++++++ .../silvermobtest/exemplary/video-app.json | 171 ++++++++++ .../silvermobtest/params/race/banner.json | 4 + .../silvermobtest/params/race/native.json | 4 + .../silvermobtest/params/race/video.json | 4 + .../supplemental/empty-seatbid-array.json | 139 +++++++++ .../supplemental/invalid-response.json | 118 +++++++ .../invalid-silvermob-ext-object.json | 28 ++ .../supplemental/status-code-bad-request.json | 99 ++++++ .../supplemental/status-code-no-content.json | 83 +++++ .../supplemental/status-code-other-error.json | 87 ++++++ .../status-code-service-unavailable.json | 87 ++++++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_silvermob.go | 7 + static/bidder-info/silvermob.yaml | 8 + static/bidder-params/silvermob.json | 17 + usersync/usersyncers/syncer_test.go | 1 + 24 files changed, 1732 insertions(+) create mode 100644 adapters/silvermob/params_test.go create mode 100644 adapters/silvermob/silvermob.go create mode 100644 adapters/silvermob/silvermob_test.go create mode 100644 adapters/silvermob/silvermobtest/exemplary/banner-app.json create mode 100644 adapters/silvermob/silvermobtest/exemplary/banner-multi-app.json create mode 100644 adapters/silvermob/silvermobtest/exemplary/native-app.json create mode 100644 adapters/silvermob/silvermobtest/exemplary/video-app.json create mode 100644 adapters/silvermob/silvermobtest/params/race/banner.json create mode 100644 adapters/silvermob/silvermobtest/params/race/native.json create mode 100644 adapters/silvermob/silvermobtest/params/race/video.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/empty-seatbid-array.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/invalid-response.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/invalid-silvermob-ext-object.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/status-code-bad-request.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/status-code-no-content.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/status-code-other-error.json create mode 100644 adapters/silvermob/silvermobtest/supplemental/status-code-service-unavailable.json create mode 100644 openrtb_ext/imp_silvermob.go create mode 100644 static/bidder-info/silvermob.yaml create mode 100644 static/bidder-params/silvermob.json diff --git a/adapters/silvermob/params_test.go b/adapters/silvermob/params_test.go new file mode 100644 index 00000000000..13009f6a08b --- /dev/null +++ b/adapters/silvermob/params_test.go @@ -0,0 +1,56 @@ +package silvermob + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// TestValidParams makes sure that the silvermob schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderSilverMob, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected silvermob params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the silvermob schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderSilverMob, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"zoneid": "16", "host": "us"}`, + `{"zoneid": "16", "host": "eu"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"ZoneID": "asd", "Host": "123"}`, + `{}`, + `{"ZoneID": "asd"}`, + `{"Host": "111"}`, + `{"zoneid": 16, "host": 111}`, +} diff --git a/adapters/silvermob/silvermob.go b/adapters/silvermob/silvermob.go new file mode 100644 index 00000000000..be7a1762d23 --- /dev/null +++ b/adapters/silvermob/silvermob.go @@ -0,0 +1,188 @@ +package silvermob + +import ( + "encoding/json" + "fmt" + "github.com/golang/glog" + "net/http" + "text/template" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type SilverMobAdapter struct { + endpoint template.Template +} + +func NewSilverMobBidder(endpointTemplate string) *SilverMobAdapter { + template, err := template.New("endpointTemplate").Parse(endpointTemplate) + if err != nil { + glog.Fatal("Unable to parse endpoint url template") + return nil + } + return &SilverMobAdapter{endpoint: *template} +} + +func GetHeaders(request *openrtb.BidRequest) *http.Header { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("X-Openrtb-Version", "2.5") + + if request.Device != nil { + if len(request.Device.UA) > 0 { + headers.Add("User-Agent", request.Device.UA) + } + + if len(request.Device.IPv6) > 0 { + headers.Add("X-Forwarded-For", request.Device.IPv6) + } + + if len(request.Device.IP) > 0 { + headers.Add("X-Forwarded-For", request.Device.IP) + } + } + + return &headers +} + +func (a *SilverMobAdapter) MakeRequests( + openRTBRequest *openrtb.BidRequest, + reqInfo *adapters.ExtraRequestInfo, +) ( + []*adapters.RequestData, + []error, +) { + requestCopy := *openRTBRequest + impCount := len(openRTBRequest.Imp) + requestData := make([]*adapters.RequestData, 0, impCount) + errs := []error{} + + var err error + + for _, imp := range openRTBRequest.Imp { + var silvermobExt *openrtb_ext.ExtSilverMob + + silvermobExt, err = a.getImpressionExt(&imp) + + if err != nil { + errs = append(errs, err) + continue + } + + url, err := a.buildEndpointURL(silvermobExt) + if err != nil { + errs = append(errs, err) + continue + } + + requestCopy.Imp = []openrtb.Imp{imp} + reqJSON, err := json.Marshal(requestCopy) + if err != nil { + errs = append(errs, err) + continue + } + + reqData := &adapters.RequestData{ + Method: http.MethodPost, + Body: reqJSON, + Uri: url, + Headers: *GetHeaders(&requestCopy), + } + + requestData = append(requestData, reqData) + } + + return requestData, errs +} + +func (a *SilverMobAdapter) getImpressionExt(imp *openrtb.Imp) (*openrtb_ext.ExtSilverMob, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("error unmarshaling imp.ext: %s", err.Error()), + } + } + var silvermobExt openrtb_ext.ExtSilverMob + if err := json.Unmarshal(bidderExt.Bidder, &silvermobExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("error unmarshaling imp.ext.bidder: %s", err.Error()), + } + } + return &silvermobExt, nil +} + +func (a *SilverMobAdapter) buildEndpointURL(params *openrtb_ext.ExtSilverMob) (string, error) { + endpointParams := macros.EndpointTemplateParams{ZoneID: params.ZoneID, Host: params.Host} + return macros.ResolveMacros(a.endpoint, endpointParams) +} + +func (a *SilverMobAdapter) MakeBids( + openRTBRequest *openrtb.BidRequest, + requestToBidder *adapters.RequestData, + bidderRawResponse *adapters.ResponseData, +) ( + bidderResponse *adapters.BidderResponse, + errs []error, +) { + + if bidderRawResponse.StatusCode == http.StatusNoContent { + return nil, nil + } + + if bidderRawResponse.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Bad Request status code: %d. Run with request.debug = 1 for more info", bidderRawResponse.StatusCode), + }} + } + + if bidderRawResponse.StatusCode != http.StatusOK { + return nil, []error{fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", bidderRawResponse.StatusCode)} + } + + responseBody := bidderRawResponse.Body + var bidResp openrtb.BidResponse + if err := json.Unmarshal(responseBody, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Error unmarshaling server Response: %s", err), + }} + } + + if len(bidResp.SeatBid) == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Empty SeatBid array", + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, sb := range bidResp.SeatBid { + for _, bid := range sb.Bid { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp), + }) + } + } + + return bidResponse, nil +} + +func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impId { + if imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } else if imp.Native != nil { + mediaType = openrtb_ext.BidTypeNative + } + return mediaType + } + } + return mediaType +} diff --git a/adapters/silvermob/silvermob_test.go b/adapters/silvermob/silvermob_test.go new file mode 100644 index 00000000000..f75b16fe3c2 --- /dev/null +++ b/adapters/silvermob/silvermob_test.go @@ -0,0 +1,11 @@ +package silvermob + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "silvermobtest", NewSilverMobBidder("http://{{.Host}}.example.com/api/dsp/bid/{{.ZoneID}}")) +} diff --git a/adapters/silvermob/silvermobtest/exemplary/banner-app.json b/adapters/silvermob/silvermobtest/exemplary/banner-app.json new file mode 100644 index 00000000000..7a2a4fef26e --- /dev/null +++ b/adapters/silvermob/silvermobtest/exemplary/banner-app.json @@ -0,0 +1,163 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w":320, + "h":50 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w":320, + "h":50 + } + ], + "type": "banner", + "seat": "silvermob" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid":{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w":320, + "h":50 + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/silvermob/silvermobtest/exemplary/banner-multi-app.json b/adapters/silvermob/silvermobtest/exemplary/banner-multi-app.json new file mode 100644 index 00000000000..531704c5c29 --- /dev/null +++ b/adapters/silvermob/silvermobtest/exemplary/banner-multi-app.json @@ -0,0 +1,293 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + }, + { + "id": "another-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":480 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w":320, + "h":50 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w":320, + "h":50 + } + ], + "type": "banner", + "seat": "silvermob" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + }, + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "another-impression-id", + "banner": { + "w":320, + "h":480 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "1" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e162", + "impid": "another-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "21", + "w":320, + "h":480 + } + ], + "type": "banner", + "seat": "silvermob" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + + { + "bids":[ + { + "bid":{ + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w":320, + "h":50 + }, + "type": "banner" + } + ] + }, + { + "bids": [ + { + "bid":{ + "id": "a3ae1b4e2fc24a4fb45540082e98e162", + "impid": "another-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "21", + "w":320, + "h":480 + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/silvermob/silvermobtest/exemplary/native-app.json b/adapters/silvermob/silvermobtest/exemplary/native-app.json new file mode 100644 index 00000000000..49934bf75ab --- /dev/null +++ b/adapters/silvermob/silvermobtest/exemplary/native-app.json @@ -0,0 +1,159 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20" + } + ], + "type": "native", + "seat": "silvermob" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "asesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ] + }, + "type": "native" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/silvermob/silvermobtest/exemplary/video-app.json b/adapters/silvermob/silvermobtest/exemplary/video-app.json new file mode 100644 index 00000000000..c80d5ab900a --- /dev/null +++ b/adapters/silvermob/silvermobtest/exemplary/video-app.json @@ -0,0 +1,171 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "asesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720 + } + ], + "seat": "silvermob" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "asesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 1280, + "h": 720 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/silvermob/silvermobtest/params/race/banner.json b/adapters/silvermob/silvermobtest/params/race/banner.json new file mode 100644 index 00000000000..9b6ca9d749b --- /dev/null +++ b/adapters/silvermob/silvermobtest/params/race/banner.json @@ -0,0 +1,4 @@ +{ + "zoneid": "0", + "host": "eu" +} diff --git a/adapters/silvermob/silvermobtest/params/race/native.json b/adapters/silvermob/silvermobtest/params/race/native.json new file mode 100644 index 00000000000..f63a4842b6d --- /dev/null +++ b/adapters/silvermob/silvermobtest/params/race/native.json @@ -0,0 +1,4 @@ +{ + "zoneid": "0", + "host": "eu" +} \ No newline at end of file diff --git a/adapters/silvermob/silvermobtest/params/race/video.json b/adapters/silvermob/silvermobtest/params/race/video.json new file mode 100644 index 00000000000..f63a4842b6d --- /dev/null +++ b/adapters/silvermob/silvermobtest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "zoneid": "0", + "host": "eu" +} \ No newline at end of file diff --git a/adapters/silvermob/silvermobtest/supplemental/empty-seatbid-array.json b/adapters/silvermob/silvermobtest/supplemental/empty-seatbid-array.json new file mode 100644 index 00000000000..be95abeaa2f --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/empty-seatbid-array.json @@ -0,0 +1,139 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "silvermob": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Empty SeatBid array", + "comparison": "literal" + } + ] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/invalid-response.json b/adapters/silvermob/silvermobtest/supplemental/invalid-response.json new file mode 100644 index 00000000000..d2a1e890df0 --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/invalid-response.json @@ -0,0 +1,118 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Openrtb-Version": [ + "2.5" + ], + "User-Agent": [ + "test-user-agent" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w":320, + "h":50 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Error unmarshaling server Response: json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/invalid-silvermob-ext-object.json b/adapters/silvermob/silvermobtest/supplemental/invalid-silvermob-ext-object.json new file mode 100644 index 00000000000..090d7aff5b6 --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/invalid-silvermob-ext-object.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": "Awesome" + } + ], + "site": { + "page": "test.com" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "error unmarshaling imp.ext: json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/status-code-bad-request.json b/adapters/silvermob/silvermobtest/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..e93f249030d --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/status-code-bad-request.json @@ -0,0 +1,99 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID", + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 400 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Bad Request status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/status-code-no-content.json b/adapters/silvermob/silvermobtest/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..a29710bfa85 --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/status-code-no-content.json @@ -0,0 +1,83 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/status-code-other-error.json b/adapters/silvermob/silvermobtest/supplemental/status-code-other-error.json new file mode 100644 index 00000000000..a32af01e55f --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/status-code-other-error.json @@ -0,0 +1,87 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 306 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 306. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/silvermob/silvermobtest/supplemental/status-code-service-unavailable.json b/adapters/silvermob/silvermobtest/supplemental/status-code-service-unavailable.json new file mode 100644 index 00000000000..5772a86ee8a --- /dev/null +++ b/adapters/silvermob/silvermobtest/supplemental/status-code-service-unavailable.json @@ -0,0 +1,87 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://eu.example.com/api/dsp/bid/0", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "host": "eu", + "zoneid": "0" + } + } + } + ], + "app": { + "id": "app_001", + "bundle": "com.awesome.app", + "publisher": { + "id": "2" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 503 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 503. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/config/config.go b/config/config.go index c2db7b7d03c..7d1fac3c633 100755 --- a/config/config.go +++ b/config/config.go @@ -992,6 +992,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rubicon.disabled", true) v.SetDefault("adapters.rubicon.endpoint", "http://exapi-us-east.rubiconproject.com/a/api/exchange.json") v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") + v.SetDefault("adapters.silvermob.endpoint", "http://{{.Host}}.silvermob.com/marketplace/api/dsp/bid/{{.ZoneID}}") v.SetDefault("adapters.smaato.endpoint", "https://prebid.ad.smaato.net/oapi/prebid") v.SetDefault("adapters.smartadserver.endpoint", "https://ssb-global.smartadserver.com") v.SetDefault("adapters.smartrtb.endpoint", "http://market-east.smrtb.com/json/publisher/rtb?pubid={{.PublisherID}}") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index ff72165bcb3..9800ccd7e28 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -74,6 +74,7 @@ import ( "github.com/prebid/prebid-server/adapters/rtbhouse" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/adapters/sharethrough" + "github.com/prebid/prebid-server/adapters/silvermob" "github.com/prebid/prebid-server/adapters/smaato" "github.com/prebid/prebid-server/adapters/smartadserver" "github.com/prebid/prebid-server/adapters/smartrtb" @@ -176,6 +177,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker), openrtb_ext.BidderSharethrough: sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint), + openrtb_ext.BidderSilverMob: silvermob.NewSilverMobBidder(cfg.Adapters[string(openrtb_ext.BidderSilverMob)].Endpoint), openrtb_ext.BidderSmaato: smaato.NewSmaatoBidder(cfg.Adapters[string(openrtb_ext.BidderSmaato)].Endpoint), openrtb_ext.BidderSmartadserver: smartadserver.NewSmartadserverBidder(cfg.Adapters[string(openrtb_ext.BidderSmartadserver)].Endpoint), openrtb_ext.BidderSmartRTB: smartrtb.NewSmartRTBBidder(cfg.Adapters[string(openrtb_ext.BidderSmartRTB)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index b98c80ae9af..0fac0800ee3 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -93,6 +93,7 @@ const ( BidderRTBHouse BidderName = "rtbhouse" BidderRubicon BidderName = "rubicon" BidderSharethrough BidderName = "sharethrough" + BidderSilverMob BidderName = "silvermob" BidderSmaato BidderName = "smaato" BidderSmartadserver BidderName = "smartadserver" BidderSmartRTB BidderName = "smartrtb" @@ -187,6 +188,7 @@ var BidderMap = map[string]BidderName{ "rtbhouse": BidderRTBHouse, "rubicon": BidderRubicon, "sharethrough": BidderSharethrough, + "silvermob": BidderSilverMob, "smaato": BidderSmaato, "smartadserver": BidderSmartadserver, "smartrtb": BidderSmartRTB, diff --git a/openrtb_ext/imp_silvermob.go b/openrtb_ext/imp_silvermob.go new file mode 100644 index 00000000000..9b2465534ca --- /dev/null +++ b/openrtb_ext/imp_silvermob.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtSilverMob defines the contract for bidrequest.imp[i].ext.silvermob +type ExtSilverMob struct { + ZoneID string `json:"zoneid"` + Host string `json:"host"` +} diff --git a/static/bidder-info/silvermob.yaml b/static/bidder-info/silvermob.yaml new file mode 100644 index 00000000000..5f1e4809dd3 --- /dev/null +++ b/static/bidder-info/silvermob.yaml @@ -0,0 +1,8 @@ +maintainer: + email: "support@silvermob.com" +capabilities: + app: + mediaTypes: + - banner + - video + - native \ No newline at end of file diff --git a/static/bidder-params/silvermob.json b/static/bidder-params/silvermob.json new file mode 100644 index 00000000000..8ebc85a2ab7 --- /dev/null +++ b/static/bidder-params/silvermob.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "SilverMob Adapter Params", + "description": "A schema which validates params accepted by the SilverMob adapter", + "type": "object", + "properties": { + "zoneid": { + "type": "string", + "description": "Zone ID" + }, + "host": { + "type": "string", + "description": "Host" + } + }, + "required": ["zoneid", "host"] + } \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 7582055fa46..a6e74966ad7 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -102,6 +102,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderMobileFuse: true, openrtb_ext.BidderOrbidder: true, openrtb_ext.BidderPubnative: true, + openrtb_ext.BidderSilverMob: true, openrtb_ext.BidderSmaato: true, openrtb_ext.BidderTappx: true, openrtb_ext.BidderYeahmobi: true, From 9a3f2a042aff31127037d5f80531edee6fd560d3 Mon Sep 17 00:00:00 2001 From: Seba Perez Date: Wed, 11 Nov 2020 15:28:33 -0300 Subject: [PATCH 249/318] Updated ePlanning GVL ID (#1574) --- adapters/eplanning/usersync.go | 2 +- adapters/eplanning/usersync_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/eplanning/usersync.go b/adapters/eplanning/usersync.go index 252c106a77c..faa7fa82a19 100644 --- a/adapters/eplanning/usersync.go +++ b/adapters/eplanning/usersync.go @@ -8,5 +8,5 @@ import ( ) func NewEPlanningSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("eplanning", 0, temp, adapters.SyncTypeIframe) + return adapters.NewSyncer("eplanning", 90, temp, adapters.SyncTypeIframe) } diff --git a/adapters/eplanning/usersync_test.go b/adapters/eplanning/usersync_test.go index 890832bafc3..85770689024 100644 --- a/adapters/eplanning/usersync_test.go +++ b/adapters/eplanning/usersync_test.go @@ -20,6 +20,6 @@ func TestEPlanningSyncer(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "https://ads.us.e-planning.net/uspd/1/?du=https%3A%2F%2Fads.us.e-planning.net%2Fgetuid%2F1%2F5a1ad71d2d53a0f5%3Flocalhost%2Fsetuid%3Fbidder%3Deplanning%26gdpr%3D%26gdpr_consent%3D%26uid%3D%24UID", syncInfo.URL) assert.Equal(t, "iframe", syncInfo.Type) - assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.EqualValues(t, 90, syncer.GDPRVendorID()) assert.Equal(t, false, syncInfo.SupportCORS) } From aaecdfada00fa657fcb4237ea2c4caf3b2f664e6 Mon Sep 17 00:00:00 2001 From: Sergio Date: Wed, 11 Nov 2020 22:02:59 +0100 Subject: [PATCH 250/318] update adpone google vendor id (#1577) --- adapters/adpone/usersync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/adpone/usersync.go b/adapters/adpone/usersync.go index 480ecb82f3f..67d4c998275 100644 --- a/adapters/adpone/usersync.go +++ b/adapters/adpone/usersync.go @@ -7,7 +7,7 @@ import ( "github.com/prebid/prebid-server/usersync" ) -const adponeGDPRVendorID = uint16(16) +const adponeGDPRVendorID = uint16(799) const adponeFamilyName = "adpone" func NewadponeSyncer(urlTemplate *template.Template) usersync.Usersyncer { From 70600225899ce1b55e1ea19a6c07d1172b1bd642 Mon Sep 17 00:00:00 2001 From: Gena Date: Thu, 12 Nov 2020 17:43:05 +0200 Subject: [PATCH 251/318] ADtelligent gvlid (#1581) --- adapters/adtelligent/usersync.go | 2 +- adapters/adtelligent/usersync_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/adtelligent/usersync.go b/adapters/adtelligent/usersync.go index cda3b62a071..087b5bdd22d 100644 --- a/adapters/adtelligent/usersync.go +++ b/adapters/adtelligent/usersync.go @@ -8,5 +8,5 @@ import ( ) func NewAdtelligentSyncer(temp *template.Template) usersync.Usersyncer { - return adapters.NewSyncer("adtelligent", 0, temp, adapters.SyncTypeRedirect) + return adapters.NewSyncer("adtelligent", 410, temp, adapters.SyncTypeRedirect) } diff --git a/adapters/adtelligent/usersync_test.go b/adapters/adtelligent/usersync_test.go index 1cc5dfe4627..fa157d226c5 100644 --- a/adapters/adtelligent/usersync_test.go +++ b/adapters/adtelligent/usersync_test.go @@ -25,6 +25,6 @@ func TestAdtelligentSyncer(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "//sync.adtelligent.com/csync?t=p&ep=0&redir=localhost%2Fsetuid%3Fbidder%3Dadtelligent%26gdpr%3D0%26gdpr_consent%3D%26uid%3D%7Buid%7D", syncInfo.URL) assert.Equal(t, "redirect", syncInfo.Type) - assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.EqualValues(t, 410, syncer.GDPRVendorID()) assert.Equal(t, false, syncInfo.SupportCORS) } From 12d96a6ef68161e0db498215d99410d5d64cc331 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Thu, 12 Nov 2020 13:54:35 -0500 Subject: [PATCH 252/318] Add account/ host GDPR enabled flags & account per request type GDPR enabled flags (#1564) * Add account level request type specific and general GDPR enabled flags * Clean up test TestAccountLevelGDPREnabled * Add host-level GDPR enabled flag * Move account GDPR enable check as receiver method on accountGDPR * Remove mapstructure annotations on account structs * Minor test updates * Re-add mapstructure annotations on account structs * Change RequestType to IntegrationType and struct annotation formatting * Update comment * Update account IntegrationType comments * Remove extra space in config/accounts.go via gofmt --- config/accounts.go | 52 ++++++++ config/accounts_test.go | 116 +++++++++++++++++ config/config.go | 2 + exchange/exchange.go | 2 +- exchange/exchange_test.go | 2 + .../exchangetest/gdpr-geo-eu-off-device.json | 1 + exchange/exchangetest/gdpr-geo-eu-off.json | 1 + .../gdpr-geo-eu-on-featureflag-off.json | 62 +++++++++ exchange/exchangetest/gdpr-geo-eu-on.json | 1 + exchange/utils.go | 23 +++- exchange/utils_test.go | 123 ++++++++++++++---- 11 files changed, 358 insertions(+), 27 deletions(-) create mode 100644 config/accounts_test.go create mode 100644 exchange/exchangetest/gdpr-geo-eu-on-featureflag-off.json diff --git a/config/accounts.go b/config/accounts.go index 162818eb95d..5ec818b843f 100644 --- a/config/accounts.go +++ b/config/accounts.go @@ -1,9 +1,61 @@ package config +// IntegrationType enumerates the values of integrations Prebid Server can configure for an account +type IntegrationType string + +// Possible values of integration types Prebid Server can configure for an account +const ( + IntegrationTypeAMP IntegrationType = "amp" + IntegrationTypeApp IntegrationType = "app" + IntegrationTypeVideo IntegrationType = "video" + IntegrationTypeWeb IntegrationType = "web" +) + // Account represents a publisher account configuration type Account struct { ID string `mapstructure:"id" json:"id"` Disabled bool `mapstructure:"disabled" json:"disabled"` CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"` EventsEnabled bool `mapstructure:"events_enabled" json:"events_enabled"` + GDPR AccountGDPR `mapstructure:"gdpr" json:"gdpr"` +} + +// AccountGDPR represents account-specific GDPR configuration +type AccountGDPR struct { + Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` + IntegrationEnabled AccountGDPRIntegration `mapstructure:"integration_enabled" json:"integration_enabled"` +} + +// EnabledForIntegrationType indicates whether GDPR is turned on at the account level for the specified integration type +// by using the integration type setting if defined or the general GDPR setting if defined; otherwise it returns nil +func (a *AccountGDPR) EnabledForIntegrationType(integrationType IntegrationType) *bool { + var integrationEnabled *bool + + switch integrationType { + case IntegrationTypeAMP: + integrationEnabled = a.IntegrationEnabled.AMP + case IntegrationTypeApp: + integrationEnabled = a.IntegrationEnabled.App + case IntegrationTypeVideo: + integrationEnabled = a.IntegrationEnabled.Video + case IntegrationTypeWeb: + integrationEnabled = a.IntegrationEnabled.Web + } + + if integrationEnabled != nil { + return integrationEnabled + } + if a.Enabled != nil { + return a.Enabled + } + + return nil +} + +// AccountGDPRIntegration indicates whether GDPR is enabled for each integration type +type AccountGDPRIntegration struct { + AMP *bool `mapstructure:"amp" json:"amp,omitempty"` + App *bool `mapstructure:"app" json:"app,omitempty"` + Video *bool `mapstructure:"video" json:"video,omitempty"` + Web *bool `mapstructure:"web" json:"web,omitempty"` } diff --git a/config/accounts_test.go b/config/accounts_test.go new file mode 100644 index 00000000000..ca7e893835f --- /dev/null +++ b/config/accounts_test.go @@ -0,0 +1,116 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAccountGDPREnabledForIntegrationType(t *testing.T) { + trueValue, falseValue := true, false + + tests := []struct { + description string + giveIntegrationType IntegrationType + giveGDPREnabled *bool + giveAMPGDPREnabled *bool + giveAppGDPREnabled *bool + giveVideoGDPREnabled *bool + giveWebGDPREnabled *bool + wantEnabled *bool + }{ + { + description: "GDPR AMP integration enabled, general GDPR disabled", + giveIntegrationType: IntegrationTypeAMP, + giveGDPREnabled: &falseValue, + giveAMPGDPREnabled: &trueValue, + wantEnabled: &trueValue, + }, + { + description: "GDPR App integration enabled, general GDPR disabled", + giveIntegrationType: IntegrationTypeApp, + giveGDPREnabled: &falseValue, + giveAppGDPREnabled: &trueValue, + wantEnabled: &trueValue, + }, + { + description: "GDPR Video integration enabled, general GDPR disabled", + giveIntegrationType: IntegrationTypeVideo, + giveGDPREnabled: &falseValue, + giveVideoGDPREnabled: &trueValue, + wantEnabled: &trueValue, + }, + { + description: "GDPR Web integration enabled, general GDPR disabled", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: &falseValue, + giveWebGDPREnabled: &trueValue, + wantEnabled: &trueValue, + }, + { + description: "Web integration enabled, general GDPR unspecified", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: nil, + giveWebGDPREnabled: &trueValue, + wantEnabled: &trueValue, + }, + { + description: "GDPR Web integration disabled, general GDPR enabled", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: &trueValue, + giveWebGDPREnabled: &falseValue, + wantEnabled: &falseValue, + }, + { + description: "GDPR Web integration disabled, general GDPR unspecified", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: nil, + giveWebGDPREnabled: &falseValue, + wantEnabled: &falseValue, + }, + { + description: "GDPR Web integration unspecified, general GDPR disabled", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: &falseValue, + giveWebGDPREnabled: nil, + wantEnabled: &falseValue, + }, + { + description: "GDPR Web integration unspecified, general GDPR enabled", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: &trueValue, + giveWebGDPREnabled: nil, + wantEnabled: &trueValue, + }, + { + description: "GDPR Web integration unspecified, general GDPR unspecified", + giveIntegrationType: IntegrationTypeWeb, + giveGDPREnabled: nil, + giveWebGDPREnabled: nil, + wantEnabled: nil, + }, + } + + for _, tt := range tests { + account := Account{ + GDPR: AccountGDPR{ + Enabled: tt.giveGDPREnabled, + IntegrationEnabled: AccountGDPRIntegration{ + AMP: tt.giveAMPGDPREnabled, + App: tt.giveAppGDPREnabled, + Video: tt.giveVideoGDPREnabled, + Web: tt.giveWebGDPREnabled, + }, + }, + } + + enabled := account.GDPR.EnabledForIntegrationType(tt.giveIntegrationType) + + if tt.wantEnabled == nil { + assert.Nil(t, enabled, tt.description) + } else { + assert.NotNil(t, enabled, tt.description) + assert.Equal(t, *tt.wantEnabled, *enabled, tt.description) + } + } +} diff --git a/config/config.go b/config/config.go index 7d1fac3c633..045aea3c7ab 100755 --- a/config/config.go +++ b/config/config.go @@ -210,6 +210,7 @@ type Privacy struct { } type GDPR struct { + Enabled bool `mapstructure:"enabled"` HostVendorID int `mapstructure:"host_vendor_id"` UsersyncIfAmbiguous bool `mapstructure:"usersync_if_ambiguous"` Timeouts GDPRTimeouts `mapstructure:"timeouts_ms"` @@ -1028,6 +1029,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("analytics.pubstack.buffers.count", 100) v.SetDefault("analytics.pubstack.buffers.timeout", "900s") v.SetDefault("amp_timeout_adjustment_ms", 0) + v.SetDefault("gdpr.enabled", true) v.SetDefault("gdpr.host_vendor_id", 0) v.SetDefault("gdpr.usersync_if_ambiguous", false) v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0) diff --git a/exchange/exchange.go b/exchange/exchange.go index 2e8fe76fcfa..3a5f785bbc8 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -122,7 +122,7 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque // Slice of BidRequests, each a copy of the original cleaned to only contain bidder data for the named bidder blabels := make(map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels) - cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, usersyncIfAmbiguous, e.privacyConfig) + cleanRequests, aliases, privacyLabels, errs := cleanOpenRTBRequests(ctx, bidRequest, requestExt, usersyncs, blabels, labels, e.gDPR, usersyncIfAmbiguous, e.privacyConfig, account) e.me.RecordRequestPrivacy(privacyLabels) diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index ae8bc3c8c9d..bd2fa1147ef 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -1305,6 +1305,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { Enforce: spec.EnforceLMT, }, GDPR: config.GDPR{ + Enabled: spec.GDPREnabled, UsersyncIfAmbiguous: !spec.AssumeGDPRApplies, EEACountriesMap: eeac, }, @@ -2444,6 +2445,7 @@ func TestUpdateHbPbCatDur(t *testing.T) { } type exchangeSpec struct { + GDPREnabled bool `json:"gdpr_enabled"` IncomingRequest exchangeRequest `json:"incomingRequest"` OutgoingRequests map[string]*bidderSpec `json:"outgoingRequests"` Response exchangeResponse `json:"response,omitempty"` diff --git a/exchange/exchangetest/gdpr-geo-eu-off-device.json b/exchange/exchangetest/gdpr-geo-eu-off-device.json index fc655de8162..f704cdd5c8e 100644 --- a/exchange/exchangetest/gdpr-geo-eu-off-device.json +++ b/exchange/exchangetest/gdpr-geo-eu-off-device.json @@ -1,5 +1,6 @@ { "assume_gdpr_applies": false, + "gdpr_enabled": true, "incomingRequest": { "ortbRequest": { "id": "some-request-id", diff --git a/exchange/exchangetest/gdpr-geo-eu-off.json b/exchange/exchangetest/gdpr-geo-eu-off.json index 27a030f11fc..24357eb7eec 100644 --- a/exchange/exchangetest/gdpr-geo-eu-off.json +++ b/exchange/exchangetest/gdpr-geo-eu-off.json @@ -1,5 +1,6 @@ { "assume_gdpr_applies": false, + "gdpr_enabled": true, "incomingRequest": { "ortbRequest": { "id": "some-request-id", diff --git a/exchange/exchangetest/gdpr-geo-eu-on-featureflag-off.json b/exchange/exchangetest/gdpr-geo-eu-on-featureflag-off.json new file mode 100644 index 00000000000..6c6ca3edc62 --- /dev/null +++ b/exchange/exchangetest/gdpr-geo-eu-on-featureflag-off.json @@ -0,0 +1,62 @@ +{ + "assume_gdpr_applies": true, + "gdpr_enabled": false, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "appnexus": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "FRA" + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": ["video/mp4"] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "user": { + "buyeruid": "some-buyer-id", + "geo": { + "country": "FRA" + } + } + }, + "bidAdjustment": 1.0 + }, + "mockResponse": { + "errors": ["appnexus-error"] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/gdpr-geo-eu-on.json b/exchange/exchangetest/gdpr-geo-eu-on.json index 4ec42fc6c70..eb42a17c936 100644 --- a/exchange/exchangetest/gdpr-geo-eu-on.json +++ b/exchange/exchangetest/gdpr-geo-eu-on.json @@ -1,5 +1,6 @@ { "assume_gdpr_applies": true, + "gdpr_enabled": true, "incomingRequest": { "ortbRequest": { "id": "some-request-id", diff --git a/exchange/utils.go b/exchange/utils.go index 63e56e841c3..f6fcc711e42 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -19,6 +19,13 @@ import ( "github.com/prebid/prebid-server/privacy/lmt" ) +var integrationTypeMap = map[pbsmetrics.RequestType]config.IntegrationType{ + pbsmetrics.ReqTypeAMP: config.IntegrationTypeAMP, + pbsmetrics.ReqTypeORTB2App: config.IntegrationTypeApp, + pbsmetrics.ReqTypeVideo: config.IntegrationTypeVideo, + pbsmetrics.ReqTypeORTB2Web: config.IntegrationTypeWeb, +} + const unknownBidder string = "" func BidderToPrebidSChains(req *openrtb_ext.ExtRequest) (map[string]*openrtb_ext.ExtRequestPrebidSChainSChain, error) { @@ -53,7 +60,8 @@ func cleanOpenRTBRequests(ctx context.Context, labels pbsmetrics.Labels, gDPR gdpr.Permissions, usersyncIfAmbiguous bool, - privacyConfig config.Privacy) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, privacyLabels pbsmetrics.PrivacyLabels, errs []error) { + privacyConfig config.Privacy, + account *config.Account) (requestsByBidder map[openrtb_ext.BidderName]*openrtb.BidRequest, aliases map[string]string, privacyLabels pbsmetrics.PrivacyLabels, errs []error) { impsByBidder, errs := splitImps(orig.Imp) if len(errs) > 0 { @@ -94,7 +102,9 @@ func cleanOpenRTBRequests(ctx context.Context, privacyLabels.COPPAEnforced = privacyEnforcement.COPPA privacyLabels.LMTEnforced = lmtEnforcer.ShouldEnforce(unknownBidder) - if gdpr == 1 { + gdprEnabled := gdprEnabled(account, privacyConfig, integrationTypeMap[labels.RType]) + + if gdpr == 1 && gdprEnabled { privacyLabels.GDPREnforced = true parsedConsent, err := vendorconsent.ParseString(consent) if err == nil { @@ -109,7 +119,7 @@ func cleanOpenRTBRequests(ctx context.Context, privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidder.String()) // GDPR - if gdpr == 1 { + if gdpr == 1 && gdprEnabled { coreBidder := resolveBidder(bidder.String(), aliases) var publisherID = labels.PubID @@ -127,6 +137,13 @@ func cleanOpenRTBRequests(ctx context.Context, return } +func gdprEnabled(account *config.Account, privacyConfig config.Privacy, integrationType config.IntegrationType) bool { + if accountEnabled := account.GDPR.EnabledForIntegrationType(integrationType); accountEnabled != nil { + return *accountEnabled + } + return privacyConfig.GDPR.Enabled +} + func extractCCPA(orig *openrtb.BidRequest, privacyConfig config.Privacy, aliases map[string]string) (privacy.PolicyEnforcer, error) { ccpaPolicy, err := ccpa.ReadFromRequest(orig) if err != nil { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 94975e82a46..14293ae20a3 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -82,7 +82,7 @@ func TestCleanOpenRTBRequests(t *testing.T) { } for _, test := range testCases { - reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + reqByBidders, _, _, err := cleanOpenRTBRequests(context.Background(), test.req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, &config.Account{}) if test.hasError { assert.NotNil(t, err, "Error shouldn't be nil") } else { @@ -179,7 +179,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, &config.Account{}) result := results["appnexus"] assert.Nil(t, errs) @@ -229,7 +229,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { Enforce: true, }, } - _, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &reqExtStruct, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + _, _, _, errs := cleanOpenRTBRequests(context.Background(), req, &reqExtStruct, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, &config.Account{}) assert.ElementsMatch(t, []error{test.expectError}, errs, test.description) } @@ -264,7 +264,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) { req := newBidRequest(t) req.Regs = &openrtb.Regs{COPPA: test.coppa} - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, config.Privacy{}, &config.Account{}) result := results["appnexus"] assert.Nil(t, errs) @@ -366,7 +366,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { extRequest = unmarshaledExt } - results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, extRequest, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, config.Privacy{}) + results, _, _, errs := cleanOpenRTBRequests(context.Background(), req, extRequest, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{}, true, config.Privacy{}, &config.Account{}) result := results["appnexus"] if test.hasError == true { @@ -943,7 +943,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig) + results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: true}, true, privacyConfig, &config.Account{}) result := results["appnexus"] assert.Nil(t, errs) @@ -959,8 +959,12 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) { } func TestCleanOpenRTBRequestsGDPR(t *testing.T) { + trueValue, falseValue := true, false + testCases := []struct { description string + gdprAccountEnabled *bool + gdprHostEnabled bool gdpr string gdprConsent string gdprScrub bool @@ -968,40 +972,96 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { expectPrivacyLabels pbsmetrics.PrivacyLabels }{ { - description: "Enforce - TCF Invalid", - gdpr: "1", - gdprConsent: "malformed", - gdprScrub: false, + description: "Enforce - TCF Invalid", + gdprAccountEnabled: &trueValue, + gdprHostEnabled: true, + gdpr: "1", + gdprConsent: "malformed", + gdprScrub: false, expectPrivacyLabels: pbsmetrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: "", }, }, { - description: "Enforce - TCF 1", - gdpr: "1", - gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", - gdprScrub: true, + description: "Enforce - TCF 1", + gdprAccountEnabled: &trueValue, + gdprHostEnabled: true, + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: true, expectPrivacyLabels: pbsmetrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: pbsmetrics.TCFVersionV1, }, }, { - description: "Enforce - TCF 2", - gdpr: "1", - gdprConsent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", - gdprScrub: true, + description: "Enforce - TCF 2", + gdprAccountEnabled: &trueValue, + gdprHostEnabled: true, + gdpr: "1", + gdprConsent: "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA", + gdprScrub: true, expectPrivacyLabels: pbsmetrics.PrivacyLabels{ GDPREnforced: true, GDPRTCFVersion: pbsmetrics.TCFVersionV2, }, }, { - description: "Not Enforce - TCF 1", - gdpr: "0", - gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", - gdprScrub: false, + description: "Not Enforce - TCF 1", + gdprAccountEnabled: &trueValue, + gdprHostEnabled: true, + gdpr: "0", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: false, + GDPRTCFVersion: "", + }, + }, + { + description: "Enforce - TCF 1; account GDPR enabled, host GDPR setting disregarded", + gdprAccountEnabled: &trueValue, + gdprHostEnabled: false, + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV1, + }, + }, + { + description: "Not Enforce - TCF 1; account GDPR disabled, host GDPR setting disregarded", + gdprAccountEnabled: &falseValue, + gdprHostEnabled: true, + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: false, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: false, + GDPRTCFVersion: "", + }, + }, + { + description: "Enforce - TCF 1; account GDPR not specified, host GDPR enabled", + gdprAccountEnabled: nil, + gdprHostEnabled: true, + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: true, + expectPrivacyLabels: pbsmetrics.PrivacyLabels{ + GDPREnforced: true, + GDPRTCFVersion: pbsmetrics.TCFVersionV1, + }, + }, + { + description: "Not Enforce - TCF 1; account GDPR not specified, host GDPR disabled", + gdprAccountEnabled: nil, + gdprHostEnabled: false, + gdpr: "1", + gdprConsent: "BONV8oqONXwgmADACHENAO7pqzAAppY", + gdprScrub: false, expectPrivacyLabels: pbsmetrics.PrivacyLabels{ GDPREnforced: false, GDPRTCFVersion: "", @@ -1018,13 +1078,30 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { privacyConfig := config.Privacy{ GDPR: config.GDPR{ + Enabled: test.gdprHostEnabled, TCF2: config.TCF2{ Enabled: true, }, }, } - results, _, privacyLabels, errs := cleanOpenRTBRequests(context.Background(), req, nil, &emptyUsersync{}, map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, pbsmetrics.Labels{}, &permissionsMock{personalInfoAllowed: !test.gdprScrub}, true, privacyConfig) + accountConfig := config.Account{ + GDPR: config.AccountGDPR{ + Enabled: test.gdprAccountEnabled, + }, + } + + results, _, privacyLabels, errs := cleanOpenRTBRequests( + context.Background(), + req, + nil, + &emptyUsersync{}, + map[openrtb_ext.BidderName]*pbsmetrics.AdapterLabels{}, + pbsmetrics.Labels{}, + &permissionsMock{personalInfoAllowed: !test.gdprScrub}, + true, + privacyConfig, + &accountConfig) result := results["appnexus"] assert.Nil(t, errs) From ada88b4f72dc3c298eec61242e527427c071a4fc Mon Sep 17 00:00:00 2001 From: Steve Alliance Date: Mon, 16 Nov 2020 07:47:51 -0500 Subject: [PATCH 253/318] DMX Bidfloor fix (#1579) --- adapters/dmx/dmx.go | 27 ++-- .../exemplary/imp-populated-banner.json | 139 ++++++++++++++++++ 2 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 adapters/dmx/dmxtest/exemplary/imp-populated-banner.json diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go index dfb97f33abb..6da7823a75f 100644 --- a/adapters/dmx/dmx.go +++ b/adapters/dmx/dmx.go @@ -168,6 +168,7 @@ func (adapter *DmxAdapter) MakeRequests(request *openrtb.BidRequest, req *adapte Body: oJson, Headers: headers, } + reqsBidder = append(reqsBidder, reqBidder) return } @@ -221,35 +222,29 @@ func (adapter *DmxAdapter) MakeBids(request *openrtb.BidRequest, externalRequest } func fetchParams(params dmxExt, inst openrtb.Imp, ins openrtb.Imp, imps []openrtb.Imp, banner *openrtb.Banner, video *openrtb.Video, intVal int8) []openrtb.Imp { + var tempimp openrtb.Imp + tempimp = inst if params.Bidder.TagId != "" { - ins = openrtb.Imp{ - ID: inst.ID, - TagID: params.Bidder.TagId, - Ext: inst.Ext, - Secure: &intVal, - } + tempimp.TagID = params.Bidder.TagId + tempimp.Secure = &intVal } if params.Bidder.DmxId != "" { - ins = openrtb.Imp{ - ID: inst.ID, - TagID: params.Bidder.DmxId, - Ext: inst.Ext, - Secure: &intVal, - } + tempimp.TagID = params.Bidder.DmxId + tempimp.Secure = &intVal } if banner != nil { - ins.Banner = banner + tempimp.Banner = banner } if video != nil { - ins.Video = video + tempimp.Video = video } - if ins.TagID == "" { + if tempimp.TagID == "" { return imps } - imps = append(imps, ins) + imps = append(imps, tempimp) return imps } diff --git a/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json b/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json new file mode 100644 index 00000000000..f2b30bb400b --- /dev/null +++ b/adapters/dmx/dmxtest/exemplary/imp-populated-banner.json @@ -0,0 +1,139 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app":{ + "bundle":"302324249", + "id":"ed6207cefff74c14878963566683c070", + "name":"Skout - iOS Match Buy", + "publisher":{ + "id":"10400" + }, + "storeurl":"https://itunes.apple.com/app/id302324249" + }, + "imp": [ + { + "bidfloor": 0.35, + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250, + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "dmxid": "123454", + "publisher_id": "10400" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "", + "body": { + "id": "test-request-id", + "app":{ + "bundle":"302324249", + "id":"ed6207cefff74c14878963566683c070", + "name":"Skout - iOS Match Buy", + "publisher":{ + "id":"10400" + }, + "storeurl":"https://itunes.apple.com/app/id302324249" + }, + "imp": [ + { + "bidfloor": 0.35, + "id": "test-imp-id", + "tagid": "123454", + "secure": 1, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisher_id": "10400", + "dmxid": "123454" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adid": "29681110", + "adm": "
banner-ads
", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300 + }] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 1.75, + "adm": "
banner-ads
", + "adid": "29681110", + "adomain": ["dmx.districtm.io"], + "iurl": "https://dmx.districtm.io/b/v2", + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250 + + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file From acf889e881afdf1085a76e45e61879e69f931320 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Tue, 17 Nov 2020 18:39:18 +0200 Subject: [PATCH 254/318] adform bidder video bid response support (#1573) --- adapters/adform/adform.go | 25 +++-- adapters/adform/adform_test.go | 67 ++++++++++--- .../exemplary/multiformat-impression.json | 99 +++++++++++++++++++ .../exemplary/single-banner-impression.json | 64 ++++++++++++ .../exemplary/single-video-impression.json | 60 +++++++++++ .../adform/adformtest/params/race/video.json | 3 + static/bidder-info/adform.yaml | 2 + 7 files changed, 302 insertions(+), 18 deletions(-) create mode 100644 adapters/adform/adformtest/exemplary/multiformat-impression.json create mode 100644 adapters/adform/adformtest/exemplary/single-banner-impression.json create mode 100644 adapters/adform/adformtest/exemplary/single-video-impression.json create mode 100644 adapters/adform/adformtest/params/race/video.json diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go index 5881f4ab86e..d7e5dedac22 100644 --- a/adapters/adform/adform.go +++ b/adapters/adform/adform.go @@ -79,6 +79,7 @@ type adformBid struct { Height uint64 `json:"height,omitempty"` DealId string `json:"deal_id,omitempty"` CreativeId string `json:"win_crid,omitempty"` + VastContent string `json:"vast_content,omitempty"` } const priceTypeGross = "gross" @@ -241,7 +242,8 @@ func toPBSBidSlice(adformBids []*adformBid, r *adformRequest) pbs.PBSBidSlice { bids := make(pbs.PBSBidSlice, 0) for i, bid := range adformBids { - if bid.Banner == "" || bid.ResponseType != "banner" { + adm, bidType := getAdAndType(bid) + if adm == "" { continue } pbsBid := pbs.PBSBid{ @@ -249,12 +251,12 @@ func toPBSBidSlice(adformBids []*adformBid, r *adformRequest) pbs.PBSBidSlice { AdUnitCode: r.adUnits[i].adUnitCode, BidderCode: r.bidderCode, Price: bid.Price, - Adm: bid.Banner, + Adm: adm, Width: bid.Width, Height: bid.Height, DealId: bid.DealId, Creative_id: bid.CreativeId, - CreativeMediaType: string(openrtb_ext.BidTypeBanner), + CreativeMediaType: string(bidType), } bids = append(bids, &pbsBid) @@ -632,21 +634,23 @@ func toOpenRtbBidResponse(adformBids []*adformBid, r *openrtb.BidRequest) *adapt } for i, bid := range adformBids { - if bid.Banner == "" || bid.ResponseType != "banner" { + adm, bidType := getAdAndType(bid) + if adm == "" { continue } + openRtbBid := openrtb.Bid{ ID: r.Imp[i].ID, ImpID: r.Imp[i].ID, Price: bid.Price, - AdM: bid.Banner, + AdM: adm, W: bid.Width, H: bid.Height, DealID: bid.DealId, CrID: bid.CreativeId, } - bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{Bid: &openRtbBid, BidType: openrtb_ext.BidTypeBanner}) + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{Bid: &openRtbBid, BidType: bidType}) currency = bid.Currency } @@ -654,3 +658,12 @@ func toOpenRtbBidResponse(adformBids []*adformBid, r *openrtb.BidRequest) *adapt return bidResponse } + +func getAdAndType(bid *adformBid) (string, openrtb_ext.BidType) { + if bid.ResponseType == "banner" { + return bid.Banner, openrtb_ext.BidTypeBanner + } else if bid.ResponseType == "vast_content" { + return bid.VastContent, openrtb_ext.BidTypeVideo + } + return "", "" +} diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index f227776207d..0bfb95b6369 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -107,6 +107,16 @@ func createAdformServerResponse(testData aBidInfo) ([]byte, error) { DealId: testData.tags[2].dealId, CreativeId: testData.tags[2].creativeId, }, + { + ResponseType: "vast_content", + VastContent: testData.tags[3].content, + Price: testData.tags[3].price, + Currency: "EUR", + Width: testData.width, + Height: testData.height, + DealId: testData.tags[3].dealId, + CreativeId: testData.tags[3].creativeId, + }, } adformServerResponse, err := json.Marshal(bids) return adformServerResponse, err @@ -123,10 +133,21 @@ func TestAdformBasicResponse(t *testing.T) { if err != nil { t.Fatalf("Should not have gotten adapter error: %v", err) } - if len(bids) != 2 { - t.Fatalf("Received %d bids instead of 2", len(bids)) + if len(bids) != 3 { + t.Fatalf("Received %d bids instead of 3", len(bids)) + } + expectedTypes := []openrtb_ext.BidType{ + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeVideo, } - for _, bid := range bids { + + for i, bid := range bids { + + if bid.CreativeMediaType != string(expectedTypes[i]) { + t.Errorf("Expected a %s bid. Got: %s", expectedTypes[i], bid.CreativeMediaType) + } + matched := false for _, tag := range adformTestData.tags { if bid.AdUnitCode == tag.code { @@ -224,7 +245,7 @@ func preparePrebidRequest(serverUrl string, t *testing.T) *pbs.PBSRequest { func preparePrebidRequestBody(requestData aBidInfo, t *testing.T) *bytes.Buffer { prebidRequest := pbs.PBSRequest{ - AdUnits: make([]pbs.AdUnit, 3), + AdUnits: make([]pbs.AdUnit, 4), Device: &openrtb.Device{ UA: requestData.deviceUA, IP: requestData.deviceIP, @@ -326,6 +347,7 @@ func createTestData(secure bool) aBidInfo { {mid: 32344, keyValues: "color:red,age:30-40", keyWords: "red,blue", cdims: "300x300,400x200", priceType: "gross", code: "code1", price: 1.23, content: "banner-content1", dealId: "dealId1", creativeId: "creativeId1"}, {mid: 32345, priceType: "net", code: "code2", minp: 23.1, cdims: "300x200"}, // no bid for ad unit {mid: 32346, code: "code3", price: 1.24, content: "banner-content2", dealId: "dealId2", url: "https://adform.com?a=b"}, + {mid: 32347, code: "code4", content: "vast-xml"}, }, secure: secure, currency: "EUR", @@ -379,6 +401,11 @@ func createOpenRtbRequest(testData *aBidInfo) *openrtb.BidRequest { func TestOpenRTBStandardResponse(t *testing.T) { testData := createTestData(true) request := createOpenRtbRequest(&testData) + expectedTypes := []openrtb_ext.BidType{ + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeVideo, + } responseBody, err := createAdformServerResponse(testData) if err != nil { @@ -390,16 +417,17 @@ func TestOpenRTBStandardResponse(t *testing.T) { bidder := new(AdformAdapter) bidResponse, errs := bidder.MakeBids(request, nil, httpResponse) - if len(bidResponse.Bids) != 2 { - t.Fatalf("Expected 2 bids. Got %d", len(bidResponse.Bids)) + if len(bidResponse.Bids) != 3 { + t.Fatalf("Expected 3 bids. Got %d", len(bidResponse.Bids)) } if len(errs) != 0 { t.Errorf("Expected 0 errors. Got %d", len(errs)) } - for _, typeBid := range bidResponse.Bids { - if typeBid.BidType != openrtb_ext.BidTypeBanner { - t.Errorf("Expected a banner bid. Got: %s", bidResponse.Bids[0].BidType) + for i, typeBid := range bidResponse.Bids { + + if typeBid.BidType != expectedTypes[i] { + t.Errorf("Expected a %s bid. Got: %s", expectedTypes[i], typeBid.BidType) } bid := typeBid.Bid matched := false @@ -561,10 +589,10 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request, isOpenRtb boo var midsWithCurrency = "" var queryString = "" if isOpenRtb { - midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9RVVSJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9RVVSJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9RVVS" + midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9RVVSJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9RVVSJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9RVVS&bWlkPTMyMzQ3JnJjdXI9RVVS" queryString = "CC=1&adid=6D92078A-8246-4BA4-AE5B-76104861E7DC&eids=eyJ0ZXN0LmNvbSI6eyJvdGhlcl91c2VyX2lkIjpbMF0sInNvbWVfdXNlcl9pZCI6WzFdfSwidGVzdDIub3JnIjp7Im90aGVyX3VzZXJfaWQiOlsyXX19&fd=1&gdpr=1&gdpr_consent=abc&ip=111.111.111.111&pt=gross&rp=4&stid=transaction-id&url=https%3A%2F%2Fadform.com%3Fa%3Db&" + midsWithCurrency } else { - midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9VVNEJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9VVNEJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9VVNE" // no way to pass currency in legacy adapter + midsWithCurrency = "bWlkPTMyMzQ0JnJjdXI9VVNEJm1rdj1jb2xvcjpyZWQsYWdlOjMwLTQwJm1rdz1yZWQsYmx1ZSZjZGltcz0zMDB4MzAwLDQwMHgyMDA&bWlkPTMyMzQ1JnJjdXI9VVNEJmNkaW1zPTMwMHgyMDAmbWlucD0yMy4xMA&bWlkPTMyMzQ2JnJjdXI9VVNE&bWlkPTMyMzQ3JnJjdXI9VVNE" // no way to pass currency in legacy adapter queryString = "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&" + midsWithCurrency } @@ -646,7 +674,7 @@ func TestPriceTypeUrlParameterCreation(t *testing.T) { // Asserts that toOpenRtbBidResponse() creates a *adapters.BidderResponse with // the currency of the last valid []*adformBid element and the expected number of bids func TestToOpenRtbBidResponse(t *testing.T) { - expectedBids := 3 + expectedBids := 4 lastCurrency, anotherCurrency, emptyCurrency := "EUR", "USD", "" request := &openrtb.BidRequest{ @@ -672,6 +700,11 @@ func TestToOpenRtbBidResponse(t *testing.T) { Ext: json.RawMessage(`{"bidder1": { "mid": "32344" }}`), Banner: &openrtb.Banner{}, }, + { + ID: "video-imp-no4", + Ext: json.RawMessage(`{"bidder1": { "mid": "32345" }}`), + Banner: &openrtb.Banner{}, + }, }, Device: &openrtb.Device{UA: "ua", IP: "ip"}, User: &openrtb.User{BuyerUID: "buyerUID"}, @@ -703,6 +736,16 @@ func TestToOpenRtbBidResponse(t *testing.T) { ResponseType: "banner", Banner: "banner-content4", Price: 1.25, + Currency: emptyCurrency, + Width: 300, + Height: 200, + DealId: "dealId4", + CreativeId: "creativeId4", + }, + { + ResponseType: "vast_content", + VastContent: "vast-content", + Price: 1.25, Currency: lastCurrency, Width: 300, Height: 200, diff --git a/adapters/adform/adformtest/exemplary/multiformat-impression.json b/adapters/adform/adformtest/exemplary/multiformat-impression.json new file mode 100644 index 00000000000..efd4aca63e2 --- /dev/null +++ b/adapters/adform/adformtest/exemplary/multiformat-impression.json @@ -0,0 +1,99 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "banner-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "mid": 12345 + } + } + }, + { + "id": "video-imp-id", + "video": { + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "mid": 54321 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE&bWlkPTU0MzIxJnJjdXI9VVNE" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "response": "banner", + "banner": "", + "win_bid": 0.5, + "win_cur": "USD", + "width": 300, + "height": 250, + "deal_id": null, + "win_crid": "20078830" + }, + { + "response": "vast_content", + "vast_content": "", + "win_bid": 0.7, + "win_cur": "USD", + "width": 640, + "height": 480, + "deal_id": "DID-123-22", + "win_crid": "20078831" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "banner-imp-id", + "impid": "banner-imp-id", + "price": 0.5, + "adm": "", + "crid": "20078830", + "w": 300, + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "id": "video-imp-id", + "impid": "video-imp-id", + "price": 0.7, + "adm": "", + "crid": "20078831", + "dealid": "DID-123-22", + "w": 640, + "h": 480 + }, + "type": "video" + } + ] + } + ] + } diff --git a/adapters/adform/adformtest/exemplary/single-banner-impression.json b/adapters/adform/adformtest/exemplary/single-banner-impression.json new file mode 100644 index 00000000000..fd7f3cde526 --- /dev/null +++ b/adapters/adform/adformtest/exemplary/single-banner-impression.json @@ -0,0 +1,64 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "mid": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "response": "banner", + "banner": "", + "win_bid": 0.5, + "win_cur": "USD", + "width": 300, + "height": 250, + "deal_id": null, + "win_crid": "20078830" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "", + "crid": "20078830", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adform/adformtest/exemplary/single-video-impression.json b/adapters/adform/adformtest/exemplary/single-video-impression.json new file mode 100644 index 00000000000..e22977e6523 --- /dev/null +++ b/adapters/adform/adformtest/exemplary/single-video-impression.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "mid": 54321 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTU0MzIxJnJjdXI9VVNE" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "response": "vast_content", + "vast_content": "", + "win_bid": 0.5, + "win_cur": "USD", + "width": 640, + "height": 480, + "deal_id": null, + "win_crid": "20078830" + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "", + "crid": "20078830", + "w": 640, + "h": 480 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/adform/adformtest/params/race/video.json b/adapters/adform/adformtest/params/race/video.json new file mode 100644 index 00000000000..51f8f1b94d2 --- /dev/null +++ b/adapters/adform/adformtest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "mid": "858300" +} diff --git a/static/bidder-info/adform.yaml b/static/bidder-info/adform.yaml index 8aafd9f6815..4dce10b9af8 100644 --- a/static/bidder-info/adform.yaml +++ b/static/bidder-info/adform.yaml @@ -4,6 +4,8 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner + - video From 17f50208439141bc14aa02fd34edc25b98a94375 Mon Sep 17 00:00:00 2001 From: Mansi Nahar Date: Tue, 17 Nov 2020 11:41:50 -0500 Subject: [PATCH 255/318] Fix Beachfront JSON tests (#1578) --- .../exemplary/minimal-banner.json | 43 ++-- .../exemplary/simple-adm-video.json | 100 ++++---- .../beachfronttest/exemplary/simple-mix.json | 136 ++++++----- .../exemplary/simple-nurl-video.json | 130 ++++++----- .../minimal-banner-empty_array-200.json | 2 +- .../supplemental/minimal-mobile-video.json | 112 ++++----- .../supplemental/minimal-site-banner.json | 171 +++++++------- .../supplemental/mobile-banner.json | 28 ++- .../supplemental/multi-banner.json | 24 +- .../supplemental/multi-video.json | 214 ++++++++---------- .../supplemental/unmarshal-error-banner.json | 2 +- ...nmarshal-error-but-another-good-video.json | 2 +- .../supplemental/unmarshal-error-video.json | 2 +- 13 files changed, 462 insertions(+), 504 deletions(-) diff --git a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json index 51ce4e9295e..6672e2af91d 100644 --- a/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json +++ b/adapters/beachfront/beachfronttest/exemplary/minimal-banner.json @@ -24,7 +24,6 @@ } ] }, - "httpCalls": [ { "expectedRequest": { @@ -57,38 +56,40 @@ "ua": "", "adapterName": "BF_PREBID_S2S", "adapterVersion": "0.9.0", - "user": { - } + "user": {} } }, "mockResponse": { "status": 200, "body": [ { - "crid":"crid_1", - "price":2.942808, - "w":300, - "h":250, - "slot":"div-gpt-ad-1460505748561-0", - "adm":"
", - "id": "some_test_ad_id_1", - "impid": "some_test_ad_id_1", - "ttl": 300, - "crid": "94395500", - "w": 300, - "price": 2.942808, - "adid": "94395500", - "h": 250 - }, - "type": "banner" - }, + "expectedBidResponses": [ { - "bid": { - "adm": "00:00:15", - "id": "some_test_ad_id_2", - "impid": "some_test_ad_id_2", - "ttl": 300, - "crid": "9999999", - "w": 1020, - "price": 1, - "adid": "9999999", - "h": 1000 + "bids": [{ + "bid": { + "adm": "
", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }, + "type": "banner" }, - "type": "video" + { + "bid": { + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + }, + "type": "video" + }] } ] } diff --git a/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json b/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json index c2b20cf1c5d..f47fd66784e 100644 --- a/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json +++ b/adapters/emx_digital/emx_digitaltest/exemplary/banner-and-video-site.json @@ -167,33 +167,33 @@ } }], - "expectedBids": [{ - "bid": { - "adm": "
", - "id": "some_test_ad_id_1", - "impid": "some_test_ad_id_1", - "ttl": 300, - "crid": "94395500", - "w": 300, - "price": 2.942808, - "adid": "94395500", - "h": 250 - }, - "type": "banner" - }, - { - "bid": { - "adm": "00:00:15", - "id": "some_test_ad_id_2", - "impid": "some_test_ad_id_2", - "ttl": 300, - "crid": "9999999", - "w": 1020, - "price": 1, - "adid": "9999999", - "h": 1000 + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "adm": "
", + "id": "some_test_ad_id_1", + "impid": "some_test_ad_id_1", + "crid": "94395500", + "w": 300, + "price": 2.942808, + "adid": "94395500", + "h": 250 + }, + "type": "banner" }, - "type": "video" + { + "bid": { + "adm": "00:00:15", + "id": "some_test_ad_id_2", + "impid": "some_test_ad_id_2", + "crid": "9999999", + "w": 1020, + "price": 1, + "adid": "9999999", + "h": 1000 + }, + "type": "video" + }] } ] } diff --git a/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json b/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json index 8de90f52192..20c62402fc1 100644 --- a/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json +++ b/adapters/emx_digital/emx_digitaltest/exemplary/banner-app.json @@ -102,18 +102,19 @@ } }], - "expectedBids": [{ - "bid": { - "adm": "
", + "adomain": [ + "dell.com.au" + ], + "cid": "668", + "crid": "253510977", + "ext": { + }, + "h": 250, + "id": "8911104898220857797", + "impid": "6a362d3a9db4eba300x250", + "nurl": "https://1x1.a-mo.net/hbx/bwin", + "price": 0.50, + "w": 300 + }, + { + "adid": "253510977", + "adm": "", + "adomain": [ + "dell.com.au" + ], + "cid": "668", + "crid": "253510977", + "ext": {}, + "h": 250, + "id": "430444686086263488", + "impid": "6a362d3a9db4eba300x250", + "nurl": "https://1x1.a-mo.net/hbx/bwin", + "price": 0.50, + "w": 300 + } + ], + "group": 0 + } + ] + } + } + }], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "adid": "253510977", + "adm": "", + "adomain": [ + "dell.com.au" + ], + "cid": "668", + "crid": "253510977", + "ext": {}, + "h": 250, + "id": "8911104898220857797", + "impid": "6a362d3a9db4eba300x250", + "nurl": "https://1x1.a-mo.net/hbx/bwin", + "price": 0.50, + "w": 300 + }, + "type": "banner" + }, + { + "bid": { + "adid": "253510977", + "adm": "", + "adomain": [ + "dell.com.au" + ], + "cid": "668", + "crid": "253510977", + "ext": {}, + "h": 250, + "id": "430444686086263488", + "impid": "6a362d3a9db4eba300x250", + "nurl": "https://1x1.a-mo.net/hbx/bwin", + "price": 0.50, + "w": 300 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/amx/amxtest/exemplary/video-simple.json b/adapters/amx/amxtest/exemplary/video-simple.json index 8fb3baa26d0..678722adf8c 100644 --- a/adapters/amx/amxtest/exemplary/video-simple.json +++ b/adapters/amx/amxtest/exemplary/video-simple.json @@ -93,7 +93,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1", "body": { "device": { "dnt": 0, diff --git a/adapters/amx/amxtest/exemplary/web-simple.json b/adapters/amx/amxtest/exemplary/web-simple.json index 74854f912ae..f75bd85acb0 100644 --- a/adapters/amx/amxtest/exemplary/web-simple.json +++ b/adapters/amx/amxtest/exemplary/web-simple.json @@ -98,7 +98,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1", "body": { "device": { "dnt": 0, diff --git a/adapters/amx/amxtest/supplemental/204-response.json b/adapters/amx/amxtest/supplemental/204-response.json index 09571a03569..f51347d34d2 100644 --- a/adapters/amx/amxtest/supplemental/204-response.json +++ b/adapters/amx/amxtest/supplemental/204-response.json @@ -47,7 +47,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1", "body": { "device": { "dnt": 0, diff --git a/adapters/amx/amxtest/supplemental/400-response.json b/adapters/amx/amxtest/supplemental/400-response.json index f10cea89718..bb20bc94e7c 100644 --- a/adapters/amx/amxtest/supplemental/400-response.json +++ b/adapters/amx/amxtest/supplemental/400-response.json @@ -47,7 +47,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1", "body": { "device": { "dnt": 0, diff --git a/adapters/amx/amxtest/supplemental/500-response.json b/adapters/amx/amxtest/supplemental/500-response.json index fe5d89930c8..c56a217ce2e 100644 --- a/adapters/amx/amxtest/supplemental/500-response.json +++ b/adapters/amx/amxtest/supplemental/500-response.json @@ -47,7 +47,7 @@ }, "httpCalls": [{ "expectedRequest": { - "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.0", + "uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1", "body": { "device": { "dnt": 0, From 3793d4c0471da364d657f1773dade1f8d572e0ed Mon Sep 17 00:00:00 2001 From: ShriprasadM Date: Thu, 28 Jan 2021 23:52:22 +0530 Subject: [PATCH 309/318] requestheaders: new parameter inside debug.httpcalls. to log request header details (#1659) * Added support for logging requestheaders inside httpCalls.requestheaders * Reverterd test case change * Modified outgoing mock request for appnexus, to send some request header information. Modified sample mock response such that ext.debug.httpcalls.appnexus.requestheaders will return the information of passed request headers * Addressed code review comments given by SyntaxNode. Also Moved RequestHeaders next to RequestBidy in openrtb_ext.ExtHttpCall Co-authored-by: Shriprasad --- exchange/bidder.go | 14 ++++++++------ exchange/bidder_test.go | 11 +++++++++++ .../request-multi-bidders-debug-info.json | 4 ++++ openrtb_ext/response.go | 9 +++++---- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/exchange/bidder.go b/exchange/bidder.go index e4867b0eeac..6e84c260f03 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -310,17 +310,19 @@ func getAssetByID(id int64, assets []nativeRequests.Asset) (nativeRequests.Asset func makeExt(httpInfo *httpCallInfo) *openrtb_ext.ExtHttpCall { if httpInfo.err == nil { return &openrtb_ext.ExtHttpCall{ - Uri: httpInfo.request.Uri, - RequestBody: string(httpInfo.request.Body), - ResponseBody: string(httpInfo.response.Body), - Status: httpInfo.response.StatusCode, + Uri: httpInfo.request.Uri, + RequestBody: string(httpInfo.request.Body), + ResponseBody: string(httpInfo.response.Body), + Status: httpInfo.response.StatusCode, + RequestHeaders: httpInfo.request.Headers, } } else if httpInfo.request == nil { return &openrtb_ext.ExtHttpCall{} } else { return &openrtb_ext.ExtHttpCall{ - Uri: httpInfo.request.Uri, - RequestBody: string(httpInfo.request.Body), + Uri: httpInfo.request.Uri, + RequestBody: string(httpInfo.request.Body), + RequestHeaders: httpInfo.request.Headers, } } } diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 8266daaa172..93aaf241a01 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -886,6 +886,9 @@ func TestBadRequestLogging(t *testing.T) { if ext.Status != 0 { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + if len(ext.RequestHeaders) > 0 { + t.Errorf("The request headers should be empty. Got %s", ext.RequestHeaders) + } } // TestBadResponseLogging makes sure that openrtb_ext works properly if we don't get a sensible HTTP response. @@ -894,6 +897,9 @@ func TestBadResponseLogging(t *testing.T) { request: &adapters.RequestData{ Uri: "test.com", Body: []byte("request body"), + Headers: http.Header{ + "header-1": []string{"value-1"}, + }, }, err: errors.New("Bad response"), } @@ -910,6 +916,7 @@ func TestBadResponseLogging(t *testing.T) { if ext.Status != 0 { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"header-1:value-1\"") } // TestSuccessfulResponseLogging makes sure that openrtb_ext works properly if the HTTP request is successful. @@ -918,6 +925,9 @@ func TestSuccessfulResponseLogging(t *testing.T) { request: &adapters.RequestData{ Uri: "test.com", Body: []byte("request body"), + Headers: http.Header{ + "header-1": []string{"value-1", "value-2"}, + }, }, response: &adapters.ResponseData{ StatusCode: 200, @@ -937,6 +947,7 @@ func TestSuccessfulResponseLogging(t *testing.T) { if ext.Status != info.response.StatusCode { t.Errorf("The Status code should be 0. Got %d", ext.Status) } + assert.Equal(t, info.request.Headers, http.Header(ext.RequestHeaders), "The request headers should be \"%s\". Got %s", info.request.Headers, ext.RequestHeaders) } func TestMobileNativeTypes(t *testing.T) { diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json index ec174f75b36..5061798bf38 100644 --- a/exchange/exchangetest/request-multi-bidders-debug-info.json +++ b/exchange/exchangetest/request-multi-bidders-debug-info.json @@ -65,6 +65,7 @@ { "uri": "appnexusTest.com", "requestbody": "appnexusTestRequestBody", + "requestheaders": { "header_1" : ["value_11", "value_12"], "header_2" : ["value_21"] }, "responsebody": "appnexusTestResponseBody", "status": 200 } @@ -94,6 +95,7 @@ { "uri": "audienceNetworkTest.com", "requestbody": "audienceNetworkTestRequestBody", + "requestheaders": null, "responsebody": "audienceNetworkTestResponseBody", "status": 200 } @@ -149,6 +151,7 @@ { "uri": "appnexusTest.com", "requestbody": "appnexusTestRequestBody", + "requestheaders": { "header_1" : ["value_11", "value_12"], "header_2" : ["value_21"] }, "responsebody": "appnexusTestResponseBody", "status": 200 } @@ -157,6 +160,7 @@ { "uri": "audienceNetworkTest.com", "requestbody": "audienceNetworkTestRequestBody", + "requestheaders": null, "responsebody": "audienceNetworkTestResponseBody", "status": 200 } diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go index 589dad40113..02370d19376 100644 --- a/openrtb_ext/response.go +++ b/openrtb_ext/response.go @@ -55,10 +55,11 @@ type ExtBidderError struct { // ExtHttpCall defines the contract for a bidresponse.ext.debug.httpcalls.{bidder}[i] type ExtHttpCall struct { - Uri string `json:"uri"` - RequestBody string `json:"requestbody"` - ResponseBody string `json:"responsebody"` - Status int `json:"status"` + Uri string `json:"uri"` + RequestBody string `json:"requestbody"` + RequestHeaders map[string][]string `json:"requestheaders"` + ResponseBody string `json:"responsebody"` + Status int `json:"status"` } // CookieStatus describes the allowed values for bidresponse.ext.usersync.{bidder}.status From c276b58e543f9a8bd7b247f11c2a054d39122035 Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Fri, 29 Jan 2021 00:16:29 +0530 Subject: [PATCH 310/318] Updating pulsepoint adapter (#1663) --- adapters/pulsepoint/params_test.go | 59 ++++++ adapters/pulsepoint/pulsepoint.go | 185 ++++++++++++++++-- adapters/pulsepoint/pulsepoint_test.go | 34 +++- .../pulsepointtest/exemplary/banner-app.json | 101 ++++++++++ .../pulsepointtest/exemplary/banner.json | 101 ++++++++++ .../exemplary/empty-pub-node-app.json | 96 +++++++++ .../exemplary/empty-pub-node-site.json | 96 +++++++++ .../pulsepointtest/exemplary/multi-imps.json | 151 ++++++++++++++ .../pulsepointtest/exemplary/native.json | 97 +++++++++ .../pulsepointtest/exemplary/video.json | 105 ++++++++++ .../pulsepointtest/params/race/banner.json | 1 - .../pulsepointtest/params/race/native.json | 4 + .../pulsepointtest/params/race/video.json | 4 + .../supplemental/bad-bid-data.json | 90 +++++++++ .../supplemental/bad-input.json | 70 +++++++ .../supplemental/bad-prebid-params.json | 32 +++ .../supplemental/bad-prebid.ext.json | 27 +++ .../pulsepointtest/supplemental/error.json | 70 +++++++ .../supplemental/impid-mismatch.json | 87 ++++++++ .../pulsepointtest/supplemental/passback.json | 68 +++++++ exchange/adapter_builders.go | 2 + exchange/adapter_util.go | 9 +- exchange/adapter_util_test.go | 34 ++-- openrtb_ext/imp_pulsepoint.go | 9 + static/bidder-info/pulsepoint.yaml | 6 + static/bidder-params/pulsepoint.json | 7 +- 26 files changed, 1487 insertions(+), 58 deletions(-) create mode 100644 adapters/pulsepoint/params_test.go create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/banner.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/native.json create mode 100644 adapters/pulsepoint/pulsepointtest/exemplary/video.json create mode 100644 adapters/pulsepoint/pulsepointtest/params/race/native.json create mode 100644 adapters/pulsepoint/pulsepointtest/params/race/video.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/error.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json create mode 100644 adapters/pulsepoint/pulsepointtest/supplemental/passback.json create mode 100644 openrtb_ext/imp_pulsepoint.go diff --git a/adapters/pulsepoint/params_test.go b/adapters/pulsepoint/params_test.go new file mode 100644 index 00000000000..ac2b314b96f --- /dev/null +++ b/adapters/pulsepoint/params_test.go @@ -0,0 +1,59 @@ +package pulsepoint + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderPulsepoint, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected pulsepoint params: %s \n Error: %s", validParam, err) + } + } +} + +// TestInvalidParams makes sure that the pubmatic schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderPulsepoint, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected pulsepoint params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"cp":1000, "ct": 2000}`, + `{"cp":1001, "ct": 2001}`, + `{"cp":1001, "ct": 2001, "cf": "1x1"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"cp":"1000"}`, + `{"ct":"1000"}`, + `{"cp":1000}`, + `{"ct":1000}`, + `{"cp":1000, "ct":"1000"}`, + `{"cp":1000, "ct": "abcd"}`, + `{"cp":"abcd", "ct": 1000}`, + `{"cp":"1000.2", "ct": "1000.1"}`, +} diff --git a/adapters/pulsepoint/pulsepoint.go b/adapters/pulsepoint/pulsepoint.go index 23f11f9a40a..b07a2cba66f 100644 --- a/adapters/pulsepoint/pulsepoint.go +++ b/adapters/pulsepoint/pulsepoint.go @@ -1,17 +1,19 @@ package pulsepoint import ( - "bytes" - "context" "encoding/json" "fmt" - "io/ioutil" "net/http" "strconv" + + "bytes" + "context" + "io/ioutil" "strings" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbs" @@ -23,15 +25,169 @@ type PulsePointAdapter struct { URI string } +// Builds an instance of PulsePointAdapter +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) { + bidder := &PulsePointAdapter{ + URI: config.Endpoint, + } + return bidder, nil +} + +func (a *PulsePointAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + errs := make([]error, 0, len(request.Imp)) + + var err error + pubID := "" + imps := make([]openrtb.Imp, 0, len(request.Imp)) + for i := 0; i < len(request.Imp); i++ { + imp := request.Imp[i] + var bidderExt adapters.ExtImpBidder + if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: err.Error(), + }) + continue + } + var pulsepointExt openrtb_ext.ExtImpPulsePoint + if err = json.Unmarshal(bidderExt.Bidder, &pulsepointExt); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: err.Error(), + }) + continue + } + // parse pubid and keep it for reference + if pubID == "" && pulsepointExt.PubID > 0 { + pubID = strconv.Itoa(pulsepointExt.PubID) + } + // tag id to be sent + imp.TagID = strconv.Itoa(pulsepointExt.TagID) + imps = append(imps, imp) + } + + // verify there are valid impressions + if len(imps) == 0 { + return nil, errs + } + + // add the publisher id from ext to the site.pub.id or app.pub.id + if request.Site != nil { + site := *request.Site + if site.Publisher != nil { + publisher := *site.Publisher + publisher.ID = pubID + site.Publisher = &publisher + } else { + site.Publisher = &openrtb.Publisher{ID: pubID} + } + request.Site = &site + } else if request.App != nil { + app := *request.App + if app.Publisher != nil { + publisher := *app.Publisher + publisher.ID = pubID + app.Publisher = &publisher + } else { + app.Publisher = &openrtb.Publisher{ID: pubID} + } + request.App = &app + } + + request.Imp = imps + reqJSON, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.URI, + Body: reqJSON, + Headers: headers, + }}, errs +} + +func (a *PulsePointAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + // passback + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + // bad requests + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Bad user input: HTTP status %d", response.StatusCode), + }} + } + // error + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode), + }} + } + // parse response + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + // map imps by id + impsByID := make(map[string]openrtb.Imp) + for i := 0; i < len(internalRequest.Imp); i++ { + impsByID[internalRequest.Imp[i].ID] = internalRequest.Imp[i] + } + + var errs []error + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + imp := impsByID[bid.ImpID] + bidType := getBidType(imp) + if &imp != nil && bidType != "" { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } + } + } + return bidResponse, errs +} + +func getBidType(imp openrtb.Imp) openrtb_ext.BidType { + // derive the bidtype purely from the impression itself + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + return openrtb_ext.BidTypeVideo + } else if imp.Audio != nil { + return openrtb_ext.BidTypeAudio + } else if imp.Native != nil { + return openrtb_ext.BidTypeNative + } + return "" +} + +///////////////////////////////// +// Legacy implementation: Start +///////////////////////////////// + +func NewPulsePointLegacyAdapter(config *adapters.HTTPAdapterConfig, uri string) *PulsePointAdapter { + a := adapters.NewHTTPAdapter(config) + + return &PulsePointAdapter{ + http: a, + URI: uri, + } +} + // used for cookies and such func (a *PulsePointAdapter) Name() string { return "pulsepoint" } -func (a *PulsePointAdapter) SkipNoCookies() bool { - return false -} - // parameters for pulsepoint adapter. type PulsepointParams struct { PublisherId int `json:"cp"` @@ -39,6 +195,10 @@ type PulsepointParams struct { AdSize string `json:"cf"` } +func (a *PulsePointAdapter) SkipNoCookies() bool { + return false +} + func (a *PulsePointAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidder *pbs.PBSBidder) (pbs.PBSBidSlice, error) { mediaTypes := []pbs.MediaType{pbs.MEDIA_TYPE_BANNER} ppReq, err := adapters.MakeOpenRTBGeneric(req, bidder, a.Name(), mediaTypes) @@ -195,11 +355,6 @@ func (a *PulsePointAdapter) Call(ctx context.Context, req *pbs.PBSRequest, bidde return bids, nil } -func NewPulsePointLegacyAdapter(config *adapters.HTTPAdapterConfig, uri string) *PulsePointAdapter { - a := adapters.NewHTTPAdapter(config) - - return &PulsePointAdapter{ - http: a, - URI: uri, - } -} +///////////////////////////////// +// Legacy implementation: End +///////////////////////////////// diff --git a/adapters/pulsepoint/pulsepoint_test.go b/adapters/pulsepoint/pulsepoint_test.go index 3eede431a12..33023d0500a 100644 --- a/adapters/pulsepoint/pulsepoint_test.go +++ b/adapters/pulsepoint/pulsepoint_test.go @@ -1,26 +1,43 @@ package pulsepoint import ( + "encoding/json" + "net/http" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/openrtb_ext" + "bytes" "context" - "encoding/json" "fmt" "io/ioutil" - "net/http" "net/http/httptest" - "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/adapterstest" "github.com/prebid/prebid-server/cache/dummycache" "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" "github.com/prebid/prebid-server/pbs" "github.com/prebid/prebid-server/usersync" ) +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderPulsepoint, config.Adapter{ + Endpoint: "http://bidder.pulsepoint.com"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "pulsepointtest", bidder) +} + +///////////////////////////////// +// Legacy implementation: Start +///////////////////////////////// + /** * Verify adapter names are setup correctly. */ @@ -75,7 +92,6 @@ func TestPulsePointOpenRTBRequest(t *testing.T) { bidder := req.Bidders[0] adapter := NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, server.URL) adapter.Call(ctx, req, bidder) - fmt.Println(service.LastBidRequest) adapterstest.VerifyIntValue(len(service.LastBidRequest.Imp), 1, t) adapterstest.VerifyStringValue(service.LastBidRequest.Imp[0].TagID, "1001", t) adapterstest.VerifyStringValue(service.LastBidRequest.Site.Publisher.ID, "2001", t) @@ -290,3 +306,7 @@ func CreateService(tagsToBid map[string]bool) adapterstest.OrtbMockService { service.LastBidRequest = &lastBidRequest return service } + +///////////////////////////////// +// Legacy implementation: End +///////////////////////////////// diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json b/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json new file mode 100644 index 00000000000..e99ca648572 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/banner-app.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "request-id", + "app": { + "bundle": "com.publisher.app", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "app": { + "bundle": "com.publisher.app", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/banner.json b/adapters/pulsepoint/pulsepointtest/exemplary/banner.json new file mode 100644 index 00000000000..d4cf797d219 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/banner.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json new file mode 100644 index 00000000000..6e16f997661 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-app.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "request-id", + "app": { + "bundle": "com.publisher.app" + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "app": { + "bundle": "com.publisher.app", + "publisher": { + "id": "1234" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json new file mode 100644 index 00000000000..6d658a2423a --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/empty-pub-node-site.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html" + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json b/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json new file mode 100644 index 00000000000..1a10355344c --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/multi-imps.json @@ -0,0 +1,151 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }, { + "id": "video-1", + "video": { + "w": 400, + "h": 300, + "mimes": ["video/x-flv"], + "minduration": 20 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }, { + "id": "video-1", + "tagid": "2001", + "video": { + "w": 400, + "h": 300, + "mimes": ["video/x-flv"], + "minduration": 20 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, { + "id": "video-1-bid", + "impid": "video-1", + "price": 4.5, + "adm": "Creative", + "adomain": [ + "advertiser.com" + ], + "crid": "201" + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "banner-1-bid", + "impid": "banner-1", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "banner" + }, { + "bid": { + "id": "video-1-bid", + "impid": "video-1", + "price": 4.5, + "adm": "Creative", + "adomain": [ + "advertiser.com" + ], + "crid": "201" + }, + "type": "video" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/native.json b/adapters/pulsepoint/pulsepointtest/exemplary/native.json new file mode 100644 index 00000000000..72c8532d783 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/native.json @@ -0,0 +1,97 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "native-1", + "native": { + "ver": "1.0", + "request": "{\"layout\":501,\"adunit\":501,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":26}},{\"id\":2,\"required\":0,\"data\":{\"type\":2,\"len\":90}}]}" + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "native-1", + "tagid": "2001", + "native": { + "ver": "1.0", + "request": "{\"layout\":501,\"adunit\":501,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":26}},{\"id\":2,\"required\":0,\"data\":{\"type\":2,\"len\":90}}]}" + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "native-1-bid", + "impid": "native-1", + "price": 3.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Adv:\"}},{\"data\":{\"type\":2,\"value\":\"Teeth Whitening\"},\"id\":2}],\"imptrackers\":[\"https://tracker.pulsepoint.com//\"],\"link\":{\"url\":\"http://click.pulsepoint.com/\"},\"ver\":\"1.0\"}}", + "adomain": [ + "advertiser.com" + ], + "crid": "20" + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "native-1-bid", + "impid": "native-1", + "price": 3.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Adv:\"}},{\"data\":{\"type\":2,\"value\":\"Teeth Whitening\"},\"id\":2}],\"imptrackers\":[\"https://tracker.pulsepoint.com//\"],\"link\":{\"url\":\"http://click.pulsepoint.com/\"},\"ver\":\"1.0\"}}", + "adomain": [ + "advertiser.com" + ], + "crid": "20" + }, + "type": "native" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/exemplary/video.json b/adapters/pulsepoint/pulsepointtest/exemplary/video.json new file mode 100644 index 00000000000..980f49f2d14 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/exemplary/video.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "video-1", + "video": { + "w": 400, + "h": 300, + "mimes": ["video/x-flv"], + "minduration": 20 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "some-request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "video-1", + "tagid": "2001", + "video": { + "w": 400, + "h": 300, + "mimes": ["video/x-flv"], + "minduration": 20 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 2001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "video-1-bid", + "impid": "video-1", + "price": 3.5, + "adm": "Creative", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [{ + "bid": { + "id": "video-1-bid", + "impid": "video-1", + "price": 3.5, + "adm": "Creative", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }, + "type": "video" + }] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/params/race/banner.json b/adapters/pulsepoint/pulsepointtest/params/race/banner.json index 1f2e34df47c..bc3e9371a5f 100644 --- a/adapters/pulsepoint/pulsepointtest/params/race/banner.json +++ b/adapters/pulsepoint/pulsepointtest/params/race/banner.json @@ -1,5 +1,4 @@ { - "cf": "300X250", "cp": 512379, "ct": 486653 } diff --git a/adapters/pulsepoint/pulsepointtest/params/race/native.json b/adapters/pulsepoint/pulsepointtest/params/race/native.json new file mode 100644 index 00000000000..57867bc27c0 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/params/race/native.json @@ -0,0 +1,4 @@ +{ + "cp": 512379, + "ct": 486655 +} diff --git a/adapters/pulsepoint/pulsepointtest/params/race/video.json b/adapters/pulsepoint/pulsepointtest/params/race/video.json new file mode 100644 index 00000000000..d8ef37eb793 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/params/race/video.json @@ -0,0 +1,4 @@ +{ + "cp": 512379, + "ct": 486654 +} diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json new file mode 100644 index 00000000000..b5209ed4bbe --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-bid-data.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-2", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": "300", + "h": "250" + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{ + "value": "json: cannot unmarshal string into Go struct field Bid.seatbid.bid.w of type uint64", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json new file mode 100644 index 00000000000..3fe1422bb00 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-input.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 400 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{ + "value": "Bad user input: HTTP status 400", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json new file mode 100644 index 00000000000..2d0c74d39d7 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid-params.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": "1234", + "ct": "1001" + } + } + }] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [{ + "value": "json: cannot unmarshal string into Go struct field ExtImpPulsePoint.cp of type int", + "comparison": "literal" + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json new file mode 100644 index 00000000000..7de9bd3c264 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/bad-prebid.ext.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": "" + }] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [{ + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/error.json b/adapters/pulsepoint/pulsepointtest/supplemental/error.json new file mode 100644 index 00000000000..c469e3bc2f9 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/error.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 503 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [{ + "value": "Bad server response: HTTP status 503", + "comparison": "literal" + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json b/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json new file mode 100644 index 00000000000..30b57e1ef60 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/impid-mismatch.json @@ -0,0 +1,87 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "response-id", + "seatbid": [{ + "bid": [{ + "id": "banner-1-bid", + "impid": "banner-2", + "price": 3.5, + "adm": "
Creative
", + "adomain": [ + "advertiser.com" + ], + "crid": "20", + "w": 300, + "h": 250 + }], + "seat": "pulsepoint-seat" + }], + "cur": "USD" + } + } + }], + "expectedBidResponses": [{ + "bids": [] + }] +} \ No newline at end of file diff --git a/adapters/pulsepoint/pulsepointtest/supplemental/passback.json b/adapters/pulsepoint/pulsepointtest/supplemental/passback.json new file mode 100644 index 00000000000..434f5a7a141 --- /dev/null +++ b/adapters/pulsepoint/pulsepointtest/supplemental/passback.json @@ -0,0 +1,68 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "123456789", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://bidder.pulsepoint.com", + "body": { + "id": "request-id", + "site": { + "page": "http://publisher.com/index.html", + "publisher": { + "id": "1234", + "name": "publisher.com" + } + }, + "imp": [{ + "id": "banner-1", + "tagid": "1001", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "cp": 1234, + "ct": 1001 + } + } + }] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 31342a4c5ff..8da57d6ec59 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -65,6 +65,7 @@ import ( "github.com/prebid/prebid-server/adapters/orbidder" "github.com/prebid/prebid-server/adapters/pubmatic" "github.com/prebid/prebid-server/adapters/pubnative" + "github.com/prebid/prebid-server/adapters/pulsepoint" "github.com/prebid/prebid-server/adapters/revcontent" "github.com/prebid/prebid-server/adapters/rhythmone" "github.com/prebid/prebid-server/adapters/rtbhouse" @@ -166,6 +167,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderOrbidder: orbidder.Builder, openrtb_ext.BidderPubmatic: pubmatic.Builder, openrtb_ext.BidderPubnative: pubnative.Builder, + openrtb_ext.BidderPulsepoint: pulsepoint.Builder, openrtb_ext.BidderRevcontent: revcontent.Builder, openrtb_ext.BidderRhythmone: rhythmone.Builder, openrtb_ext.BidderRTBHouse: rtbhouse.Builder, diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go index 6e12ed00536..4d92fe9fe9c 100644 --- a/exchange/adapter_util.go +++ b/exchange/adapter_util.go @@ -8,7 +8,6 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/pulsepoint" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -58,7 +57,7 @@ func buildBidders(adapterConfig map[string]config.Adapter, infos adapters.Bidder } // Ignore Legacy Bidders - if bidderName == openrtb_ext.BidderLifestreet || bidderName == openrtb_ext.BidderPulsepoint { + if bidderName == openrtb_ext.BidderLifestreet { continue } @@ -99,12 +98,6 @@ func buildExchangeBiddersLegacy(adapterConfig map[string]config.Adapter, infos a bidders[openrtb_ext.BidderLifestreet] = adaptLegacyAdapter(adapter) } - // Pulsepoint - if infos[string(openrtb_ext.BidderPulsepoint)].Status == adapters.StatusActive { - adapter := pulsepoint.NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, adapterConfig[string(openrtb_ext.BidderPulsepoint)].Endpoint) - bidders[openrtb_ext.BidderPulsepoint] = adaptLegacyAdapter(adapter) - } - return bidders } diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go index d7059648877..20402e1e68c 100644 --- a/exchange/adapter_util_test.go +++ b/exchange/adapter_util_test.go @@ -10,7 +10,6 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/pulsepoint" "github.com/prebid/prebid-server/adapters/rubicon" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/currency" @@ -214,9 +213,9 @@ func TestBuildBidders(t *testing.T) { }, { description: "Success - Ignores Legacy", - adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}, "pulsepoint": {}}, - bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive, "pulsepoint": infoActive}, - builders: map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder, openrtb_ext.BidderPulsepoint: inconsequentialBuilder}, + adapterConfig: map[string]config.Adapter{"appnexus": {}, "lifestreet": {}}, + bidderInfos: map[string]adapters.BidderInfo{"appnexus": infoActive, "lifestreet": infoActive}, + builders: map[openrtb_ext.BidderName]adapters.Builder{openrtb_ext.BidderAppnexus: appnexusBuilder, openrtb_ext.BidderLifestreet: inconsequentialBuilder}, expectedBidders: map[openrtb_ext.BidderName]adapters.Bidder{ openrtb_ext.BidderAppnexus: adapters.EnforceBidderInfo(appnexusBidder, infoActive), }, @@ -267,7 +266,6 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) { cfg := config.Adapter{Endpoint: "anyEndpoint"} expectedLifestreet := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")} - expectedPulsepoint := &adaptedAdapter{pulsepoint.NewPulsePointLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")} testCases := []struct { description string @@ -276,29 +274,23 @@ func TestBuildExchangeBiddersLegacy(t *testing.T) { expected map[openrtb_ext.BidderName]adaptedBidder }{ { - description: "All Active", - adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg}, - bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoActive}, - expected: map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet, "pulsepoint": expectedPulsepoint}, + description: "Active", + adapterConfig: map[string]config.Adapter{"lifestreet": cfg}, + bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoActive}, + expected: map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet}, }, { - description: "All Disabled", - adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg}, - bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoDisabled, "pulsepoint": infoDisabled}, + description: "Disabled", + adapterConfig: map[string]config.Adapter{"lifestreet": cfg}, + bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoDisabled}, expected: map[openrtb_ext.BidderName]adaptedBidder{}, }, { - description: "All Unknown", - adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg}, - bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoUnknown, "pulsepoint": infoUnknown}, + description: "Unknown", + adapterConfig: map[string]config.Adapter{"lifestreet": cfg}, + bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoUnknown}, expected: map[openrtb_ext.BidderName]adaptedBidder{}, }, - { - description: "Mixed", - adapterConfig: map[string]config.Adapter{"lifestreet": cfg, "pulsepoint": cfg}, - bidderInfos: map[string]adapters.BidderInfo{"lifestreet": infoActive, "pulsepoint": infoUnknown}, - expected: map[openrtb_ext.BidderName]adaptedBidder{"lifestreet": expectedLifestreet}, - }, } for _, test := range testCases { diff --git a/openrtb_ext/imp_pulsepoint.go b/openrtb_ext/imp_pulsepoint.go new file mode 100644 index 00000000000..c168c80f1bb --- /dev/null +++ b/openrtb_ext/imp_pulsepoint.go @@ -0,0 +1,9 @@ +package openrtb_ext + +// ExtImpPulsePoint defines the json spec for bidrequest.imp[i].ext.pulsepoint +// PubId/TagId are mandatory params + +type ExtImpPulsePoint struct { + PubID int `json:"cp"` + TagID int `json:"ct"` +} diff --git a/static/bidder-info/pulsepoint.yaml b/static/bidder-info/pulsepoint.yaml index 716e453000e..056a0bf3d7c 100644 --- a/static/bidder-info/pulsepoint.yaml +++ b/static/bidder-info/pulsepoint.yaml @@ -4,6 +4,12 @@ capabilities: app: mediaTypes: - banner + - video + - audio + - native site: mediaTypes: - banner + - video + - audio + - native diff --git a/static/bidder-params/pulsepoint.json b/static/bidder-params/pulsepoint.json index c4704c3b42e..673fd2efd6f 100644 --- a/static/bidder-params/pulsepoint.json +++ b/static/bidder-params/pulsepoint.json @@ -11,12 +11,7 @@ "ct": { "type": "integer", "description": "An ID which identifies the ad slot being sold" - }, - "cf": { - "type": "string", - "pattern": "^[0-9]+[xX][0-9]+$", - "description": "The size of the ad slot being sold. This should be a string like 300X250" } }, - "required": ["cp", "ct", "cf"] + "required": ["cp", "ct"] } From 01645db6207f4defe327767b50fcfe6a725ca340 Mon Sep 17 00:00:00 2001 From: Veronika Solovei Date: Thu, 28 Jan 2021 12:27:19 -0800 Subject: [PATCH 311/318] Debug disable feature implementation: (#1677) Co-authored-by: Veronika Solovei --- adapters/info.go | 5 + config/accounts.go | 1 + config/config.go | 1 + exchange/adapter_util.go | 7 +- exchange/adapter_util_test.go | 8 +- exchange/bidder.go | 25 +- exchange/bidder_test.go | 177 ++++++++----- exchange/bidder_validate_bids.go | 4 +- exchange/bidder_validate_bids_test.go | 10 +- exchange/exchange.go | 58 +++-- exchange/exchange_test.go | 232 +++++++++++++++--- .../exchangetest/append-bidder-names.json | 3 - exchange/exchangetest/debuglog_disabled.json | 3 - exchange/exchangetest/debuglog_enabled.json | 3 - exchange/legacy.go | 2 +- exchange/legacy_test.go | 10 +- exchange/targeting_test.go | 5 +- 17 files changed, 393 insertions(+), 161 deletions(-) diff --git a/adapters/info.go b/adapters/info.go index 7f3ad9c3af0..19897ac7031 100644 --- a/adapters/info.go +++ b/adapters/info.go @@ -206,6 +206,11 @@ type BidderInfo struct { Capabilities *CapabilitiesInfo `yaml:"capabilities" json:"capabilities"` AliasOf string `json:"aliasOf,omitempty"` ModifyingVastXmlAllowed bool `yaml:"modifyingVastXmlAllowed" json:"-" xml:"-"` + Debug *DebugInfo `yaml:"debug,omitempty" json:"-" xml:"-"` +} + +type DebugInfo struct { + Allow bool `yaml:"allow" json:"allow"` } type MaintainerInfo struct { diff --git a/config/accounts.go b/config/accounts.go index 0faf5f5f529..548092451c3 100644 --- a/config/accounts.go +++ b/config/accounts.go @@ -19,6 +19,7 @@ type Account struct { EventsEnabled bool `mapstructure:"events_enabled" json:"events_enabled"` CCPA AccountCCPA `mapstructure:"ccpa" json:"ccpa"` GDPR AccountGDPR `mapstructure:"gdpr" json:"gdpr"` + DebugAllow bool `mapstructure:"debug_allow" json:"debug_allow"` } // AccountCCPA represents account-specific CCPA configuration diff --git a/config/config.go b/config/config.go index 2a99e278deb..ea5de90d28a 100755 --- a/config/config.go +++ b/config/config.go @@ -937,6 +937,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("blacklisted_accts", []string{""}) v.SetDefault("account_required", false) v.SetDefault("account_defaults.disabled", false) + v.SetDefault("account_defaults.debug_allow", true) v.SetDefault("certificates_file", "") v.SetDefault("auto_gen_source_tid", true) diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go index 4d92fe9fe9c..7e271376868 100644 --- a/exchange/adapter_util.go +++ b/exchange/adapter_util.go @@ -38,7 +38,12 @@ func buildExchangeBidders(cfg *config.Configuration, infos adapters.BidderInfos, exchangeBidders := make(map[openrtb_ext.BidderName]adaptedBidder, len(bidders)) for bidderName, bidder := range bidders { - exchangeBidders[bidderName] = adaptBidder(bidder, client, cfg, me, bidderName) + info, infoFound := infos[string(bidderName)] + if !infoFound { + errs = append(errs, fmt.Errorf("%v: bidder info not found", bidder)) + continue + } + exchangeBidders[bidderName] = adaptBidder(bidder, client, cfg, me, bidderName, info.Debug) } return exchangeBidders, nil diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go index 20402e1e68c..92eb01291fb 100644 --- a/exchange/adapter_util_test.go +++ b/exchange/adapter_util_test.go @@ -40,7 +40,7 @@ func TestBuildAdaptersSuccess(t *testing.T) { appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{}) appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive) - appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus) + appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil) appnexusBidderValidated := addValidatedBidderMiddleware(appnexusBidderAdapted) idLegacyAdapted := &adaptedAdapter{lifestreet.NewLifestreetLegacyAdapter(adapters.DefaultHTTPAdapterConfig, "anyEndpoint")} @@ -77,11 +77,11 @@ func TestBuildExchangeBidders(t *testing.T) { appnexusBidder, _ := appnexus.Builder(openrtb_ext.BidderAppnexus, config.Adapter{}) appnexusBidderWithInfo := adapters.EnforceBidderInfo(appnexusBidder, infoActive) - appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus) + appnexusBidderAdapted := adaptBidder(appnexusBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderAppnexus, nil) rubiconBidder, _ := rubicon.Builder(openrtb_ext.BidderRubicon, config.Adapter{}) rubiconBidderWithInfo := adapters.EnforceBidderInfo(rubiconBidder, infoActive) - rubiconBidderAdapted := adaptBidder(rubiconBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderRubicon) + rubiconBidderAdapted := adaptBidder(rubiconBidderWithInfo, client, &config.Configuration{}, metricEngine, openrtb_ext.BidderRubicon, nil) testCases := []struct { description string @@ -397,7 +397,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) { type fakeAdaptedBidder struct{} -func (fakeAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { +func (fakeAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) { return nil, nil } diff --git a/exchange/bidder.go b/exchange/bidder.go index 6e84c260f03..5b5852bdd62 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -49,7 +49,7 @@ type adaptedBidder interface { // // Any errors will be user-facing in the API. // Error messages should help publishers understand what might account for "bad" bids. - requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) + requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) } // pbsOrtbBid is a Bid returned by an adaptedBidder. @@ -93,7 +93,7 @@ type pbsOrtbSeatBid struct { // // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter" // (which is being phased out and replaced by Bidder for OpenRTB auctions) -func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName) adaptedBidder { +func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me metrics.MetricsEngine, name openrtb_ext.BidderName, debugInfo *adapters.DebugInfo) adaptedBidder { return &bidderAdapter{ Bidder: bidder, BidderName: name, @@ -102,10 +102,18 @@ func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Config config: bidderAdapterConfig{ Debug: cfg.Debug, DisableConnMetrics: cfg.Metrics.Disabled.AdapterConnectionMetrics, + DebugInfo: adapters.DebugInfo{Allow: parseDebugInfo(debugInfo)}, }, } } +func parseDebugInfo(info *adapters.DebugInfo) bool { + if info == nil { + return true + } + return info.Allow +} + type bidderAdapter struct { Bidder adapters.Bidder BidderName openrtb_ext.BidderName @@ -117,9 +125,10 @@ type bidderAdapter struct { type bidderAdapterConfig struct { Debug config.Debug DisableConnMetrics bool + DebugInfo adapters.DebugInfo } -func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { +func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) { reqData, errs := bidder.Bidder.MakeRequests(request, reqInfo) if len(reqData) == 0 { @@ -155,8 +164,14 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.Bi for i := 0; i < len(reqData); i++ { httpInfo := <-responseChannel // If this is a test bid, capture debugging info from the requests. - if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) { - seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo)) + // Write debug data to ext in case if: + // - debugContextKey (url param) in true + // - account debug is allowed + // - bidder debug is allowed + if accountDebugAllowed && bidder.config.DebugInfo.Allow { + if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) { + seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo)) + } } if httpInfo.err == nil { diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index 93aaf241a01..b809c7e0f51 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -35,6 +35,16 @@ import ( // 1. The Bidder implementation is called with the arguments we expect. // 2. The returned values are correct for a non-test bid. func TestSingleBidder(t *testing.T) { + type aTest struct { + debugInfo *adapters.DebugInfo + httpCallsLen int + } + + testCases := []*aTest{ + {&adapters.DebugInfo{Allow: false}, 0}, + {&adapters.DebugInfo{Allow: true}, 1}, + } + respStatus := 200 respBody := "{\"bid\":false}" server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) @@ -46,24 +56,6 @@ func TestSingleBidder(t *testing.T) { bidAdjustment := 2.0 firstInitialPrice := 3.0 secondInitialPrice := 4.0 - mockBidderResponse := &adapters.BidderResponse{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb.Bid{ - Price: firstInitialPrice, - }, - BidType: openrtb_ext.BidTypeBanner, - DealPriority: 4, - }, - { - Bid: &openrtb.Bid{ - Price: secondInitialPrice, - }, - BidType: openrtb_ext.BidTypeVideo, - DealPriority: 5, - }, - }, - } bidderImpl := &goodSingleBidder{ httpRequest: &adapters.RequestData{ @@ -72,53 +64,81 @@ func TestSingleBidder(t *testing.T) { Body: []byte("{\"key\":\"val\"}"), Headers: http.Header{}, }, - bidResponse: mockBidderResponse, + bidResponse: nil, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) - currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) - // Make sure the goodSingleBidder was called with the expected arguments. - if bidderImpl.httpResponse == nil { - t.Errorf("The Bidder should be called with the server's response.") - } - if bidderImpl.httpResponse.StatusCode != respStatus { - t.Errorf("Bad response status. Expected %d, got %d", respStatus, bidderImpl.httpResponse.StatusCode) - } - if string(bidderImpl.httpResponse.Body) != respBody { - t.Errorf("Bad response body. Expected %s, got %s", respBody, string(bidderImpl.httpResponse.Body)) - } + ctx := context.Background() + ctx = context.WithValue(ctx, DebugContextKey, true) - // Make sure the returned values are what we expect - if len(errs) != 0 { - t.Errorf("bidder.Bid returned %d errors. Expected 0", len(errs)) - } - if len(seatBid.bids) != len(mockBidderResponse.Bids) { - t.Fatalf("Expected %d bids. Got %d", len(mockBidderResponse.Bids), len(seatBid.bids)) - } - for index, typedBid := range mockBidderResponse.Bids { - if typedBid.Bid != seatBid.bids[index].bid { - t.Errorf("Bid %d did not point to the same bid returned by the Bidder.", index) + for _, test := range testCases { + + mockBidderResponse := &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb.Bid{ + Price: firstInitialPrice, + }, + BidType: openrtb_ext.BidTypeBanner, + DealPriority: 4, + }, + { + Bid: &openrtb.Bid{ + Price: secondInitialPrice, + }, + BidType: openrtb_ext.BidTypeVideo, + DealPriority: 5, + }, + }, } - if typedBid.BidType != seatBid.bids[index].bidType { - t.Errorf("Bid %d did not have the right type. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) + bidderImpl.bidResponse = mockBidderResponse + + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, test.debugInfo) + currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) + + seatBid, errs := bidder.requestBid(ctx, &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) + + // Make sure the goodSingleBidder was called with the expected arguments. + if bidderImpl.httpResponse == nil { + t.Errorf("The Bidder should be called with the server's response.") } - if typedBid.DealPriority != seatBid.bids[index].dealPriority { - t.Errorf("Bid %d did not have the right deal priority. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) + if bidderImpl.httpResponse.StatusCode != respStatus { + t.Errorf("Bad response status. Expected %d, got %d", respStatus, bidderImpl.httpResponse.StatusCode) + } + if string(bidderImpl.httpResponse.Body) != respBody { + t.Errorf("Bad response body. Expected %s, got %s", respBody, string(bidderImpl.httpResponse.Body)) } - } - if mockBidderResponse.Bids[0].Bid.Price != bidAdjustment*firstInitialPrice { - t.Errorf("Bid[0].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*firstInitialPrice, mockBidderResponse.Bids[0].Bid.Price) - } - if mockBidderResponse.Bids[1].Bid.Price != bidAdjustment*secondInitialPrice { - t.Errorf("Bid[1].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*secondInitialPrice, mockBidderResponse.Bids[1].Bid.Price) - } - if len(seatBid.httpCalls) != 0 { - t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls)) - } - if len(seatBid.ext) != 0 { - t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext)) + // Make sure the returned values are what we expect + if len(errs) != 0 { + t.Errorf("bidder.Bid returned %d errors. Expected 0", len(errs)) + } + if len(seatBid.bids) != len(mockBidderResponse.Bids) { + t.Fatalf("Expected %d bids. Got %d", len(mockBidderResponse.Bids), len(seatBid.bids)) + } + for index, typedBid := range mockBidderResponse.Bids { + if typedBid.Bid != seatBid.bids[index].bid { + t.Errorf("Bid %d did not point to the same bid returned by the Bidder.", index) + } + if typedBid.BidType != seatBid.bids[index].bidType { + t.Errorf("Bid %d did not have the right type. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) + } + if typedBid.DealPriority != seatBid.bids[index].dealPriority { + t.Errorf("Bid %d did not have the right deal priority. Expected %s, got %s", index, typedBid.BidType, seatBid.bids[index].bidType) + } + } + if mockBidderResponse.Bids[0].Bid.Price != bidAdjustment*firstInitialPrice { + t.Errorf("Bid[0].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*firstInitialPrice, mockBidderResponse.Bids[0].Bid.Price) + } + if mockBidderResponse.Bids[1].Bid.Price != bidAdjustment*secondInitialPrice { + t.Errorf("Bid[1].Price was not adjusted properly. Expected %f, got %f", bidAdjustment*secondInitialPrice, mockBidderResponse.Bids[1].Bid.Price) + } + if len(seatBid.httpCalls) != test.httpCallsLen { + t.Errorf("The bidder shouldn't log HttpCalls when request.test == 0. Found %d", len(seatBid.httpCalls)) + } + + if len(seatBid.ext) != 0 { + t.Errorf("The bidder shouldn't define any seatBid.ext. Got %s", string(seatBid.ext)) + } } } @@ -162,9 +182,9 @@ func TestMultiBidder(t *testing.T) { }}, bidResponse: mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if seatBid == nil { t.Fatalf("SeatBid should exist, because bids exist.") @@ -524,7 +544,7 @@ func TestMultiCurrencies(t *testing.T) { ) // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -540,6 +560,7 @@ func TestMultiCurrencies(t *testing.T) { 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, + true, ) // Verify: @@ -675,7 +696,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBid, errs := bidder.requestBid( context.Background(), @@ -684,6 +705,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) { 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, + true, ) // Verify: @@ -841,7 +863,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { } // Execute: - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter( &http.Client{}, mockedHTTPServer.URL, @@ -856,6 +878,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, + true, ) // Verify: @@ -1031,7 +1054,7 @@ func TestMobileNativeTypes(t *testing.T) { }, bidResponse: tc.mockBidderResponse, } - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) seatBids, _ := bidder.requestBid( @@ -1041,6 +1064,7 @@ func TestMobileNativeTypes(t *testing.T) { 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, + true, ) var actualValue string @@ -1052,9 +1076,9 @@ func TestMobileNativeTypes(t *testing.T) { } func TestErrorReporting(t *testing.T) { - bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if bids != nil { t.Errorf("There should be no seatbid if no http requests are returned.") } @@ -1235,9 +1259,9 @@ func TestCallRecordAdapterConnections(t *testing.T) { metrics.On("RecordAdapterConnections", expectedAdapterName, false, mock.MatchedBy(compareConnWaitTime)).Once() // Run requestBid using an http.Client with a mock handler - bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus) + bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus, nil) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - _, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + _, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) // Assert no errors assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs) @@ -1412,6 +1436,23 @@ func TestTimeoutNotificationOn(t *testing.T) { assert.EqualValues(t, logExpected, logActual) } +func TestParseDebugInfoTrue(t *testing.T) { + debugInfo := &adapters.DebugInfo{Allow: true} + resDebugInfo := parseDebugInfo(debugInfo) + assert.True(t, resDebugInfo, "Debug Allow value should be true") +} + +func TestParseDebugInfoFalse(t *testing.T) { + debugInfo := &adapters.DebugInfo{Allow: false} + resDebugInfo := parseDebugInfo(debugInfo) + assert.False(t, resDebugInfo, "Debug Allow value should be false") +} + +func TestParseDebugInfoIsNil(t *testing.T) { + resDebugInfo := parseDebugInfo(nil) + assert.True(t, resDebugInfo, "Debug Allow value should be true") +} + func wrapWithBidderInfo(bidder adapters.Bidder) adapters.Bidder { bidderInfo := adapters.BidderInfo{ Status: adapters.StatusActive, diff --git a/exchange/bidder_validate_bids.go b/exchange/bidder_validate_bids.go index 3700bac4fb3..cf74dfb1dd5 100644 --- a/exchange/bidder_validate_bids.go +++ b/exchange/bidder_validate_bids.go @@ -28,8 +28,8 @@ type validatedBidder struct { bidder adaptedBidder } -func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { - seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo) +func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) { + seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo, accountDebugAllowed) if validationErrors := removeInvalidBids(request, seatBid); len(validationErrors) > 0 { errs = append(errs, validationErrors...) } diff --git a/exchange/bidder_validate_bids_test.go b/exchange/bidder_validate_bids_test.go index bf35b2db59c..e7fc0b046dd 100644 --- a/exchange/bidder_validate_bids_test.go +++ b/exchange/bidder_validate_bids_test.go @@ -42,7 +42,7 @@ func TestAllValidBids(t *testing.T) { }, }, }) - seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true) assert.Len(t, seatBid.bids, 3) assert.Len(t, errs, 0) } @@ -83,7 +83,7 @@ func TestAllBadBids(t *testing.T) { }, }, }) - seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true) assert.Len(t, seatBid.bids, 0) assert.Len(t, errs, 5) } @@ -126,7 +126,7 @@ func TestMixedBids(t *testing.T) { }, }, }) - seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true) assert.Len(t, seatBid.bids, 2) assert.Len(t, errs, 3) } @@ -246,7 +246,7 @@ func TestCurrencyBids(t *testing.T) { Cur: tc.brqCur, } - seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true) assert.Len(t, seatBid.bids, expectedValidBids) assert.Len(t, errs, expectedErrs) } @@ -257,6 +257,6 @@ type mockAdaptedBidder struct { errorResponse []error } -func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { +func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) { return b.bidResponse, b.errorResponse } diff --git a/exchange/exchange.go b/exchange/exchange.go index 99ac8a8884a..cfb9c6d39da 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -14,7 +14,6 @@ import ( "strings" "time" - uuid "github.com/gofrs/uuid" "github.com/prebid/prebid-server/stored_requests" "github.com/golang/glog" @@ -135,7 +134,15 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * _, targData.cacheHost, targData.cachePath = e.cache.GetExtCacheData() } + if debugLog == nil { + debugLog = &DebugLog{Enabled: false} + } + debugInfo := getDebugInfo(r.BidRequest, requestExt) + + debugInfo = debugInfo && r.Account.DebugAllow + debugLog.Enabled = debugLog.Enabled && r.Account.DebugAllow + if debugInfo { ctx = e.makeDebugContext(ctx, debugInfo) } @@ -163,10 +170,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * // Get currency rates conversions for the auction conversions := e.currencyConverter.Rates() - adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions) + adapterBids, adapterExtra, anyBidsReturned := e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, r.Account.DebugAllow) var auc *auction var cacheErrs []error + var bidResponseExt *openrtb_ext.ExtBidResponse if anyBidsReturned { var bidCategory map[string]string @@ -194,34 +202,35 @@ func (e *exchange) HoldAuction(ctx context.Context, r AuctionRequest, debugLog * dealErrs := applyDealSupport(r.BidRequest, auc, bidCategory) errs = append(errs, dealErrs...) } - cacheErrs := auc.doCache(ctx, e.cache, targData, evTracking, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog) + + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs) + if debugLog.Enabled { + if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + debugLog.Data.Response = string(bidRespExtBytes) + } else { + debugLog.Data.Response = "Unable to marshal response ext for debugging" + errs = append(errs, err) + } + } + + cacheErrs = auc.doCache(ctx, e.cache, targData, evTracking, r.BidRequest, 60, &r.Account.CacheTTL, bidCategory, debugLog) if len(cacheErrs) > 0 { errs = append(errs, cacheErrs...) } + targData.setTargeting(auc, r.BidRequest.App != nil, bidCategory) } - } + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs) + } else { + bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs) - bidResponseExt := e.makeExtBidResponse(adapterBids, adapterExtra, r, debugInfo, errs) + if debugLog.Enabled { - // Ensure caching errors are added in case auc.doCache was called and errors were returned - if len(cacheErrs) > 0 { - bidderCacheErrs := errsToBidderErrors(cacheErrs) - bidResponseExt.Errors[openrtb_ext.PrebidExtKey] = append(bidResponseExt.Errors[openrtb_ext.PrebidExtKey], bidderCacheErrs...) - } - - if debugLog != nil && debugLog.Enabled { - if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { - debugLog.Data.Response = string(bidRespExtBytes) - } else { - debugLog.Data.Response = "Unable to marshal response ext for debugging" - errs = append(errs, err) - } - if !anyBidsReturned { - if rawUUID, err := uuid.NewV4(); err == nil { - debugLog.CacheKey = rawUUID.String() + if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + debugLog.Data.Response = string(bidRespExtBytes) } else { + debugLog.Data.Response = "Unable to marshal response ext for debugging" errs = append(errs, err) } } @@ -342,7 +351,8 @@ func (e *exchange) getAllBids( ctx context.Context, bidderRequests []BidderRequest, bidAdjustments map[string]float64, - conversions currency.Conversions) ( + conversions currency.Conversions, + accountDebugAllowed bool) ( map[openrtb_ext.BidderName]*pbsOrtbSeatBid, map[openrtb_ext.BidderName]*seatResponseExtra, bool) { // Set up pointers to the bid results @@ -373,7 +383,7 @@ func (e *exchange) getAllBids( } var reqInfo adapters.ExtraRequestInfo reqInfo.PbsEntryPoint = bidderRequest.BidderLabels.RType - bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo) + bids, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest.BidRequest, bidderRequest.BidderName, adjustmentFactor, conversions, &reqInfo, accountDebugAllowed) // Add in time reporting elapsed := time.Since(start) @@ -758,7 +768,7 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*pb for bidderName, responseExtra := range adapterExtra { - if debugInfo { + if debugInfo && len(responseExtra.HttpCalls) > 0 { bidResponseExt.Debug.HttpCalls[bidderName] = responseExtra.HttpCalls } // Only make an entry for bidder errors if the bidder reported any. diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 416875a181c..8bdf6c451e0 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -165,41 +165,66 @@ func TestDebugBehaviour(t *testing.T) { type outTest struct { debugInfoIncluded bool } + + type debugData struct { + bidderLevelDebugAllowed bool + accountLevelDebugAllowed bool + } + type aTest struct { - desc string - in inTest - out outTest + desc string + in inTest + out outTest + debugData debugData } testCases := []aTest{ { - desc: "test flag equals zero, ext debug flag false, no debug info expected", - in: inTest{test: 0, debug: false}, - out: outTest{debugInfoIncluded: false}, + desc: "test flag equals zero, ext debug flag false, no debug info expected", + in: inTest{test: 0, debug: false}, + out: outTest{debugInfoIncluded: false}, + debugData: debugData{true, true}, }, { - desc: "test flag equals zero, ext debug flag true, debug info expected", - in: inTest{test: 0, debug: true}, - out: outTest{debugInfoIncluded: true}, + desc: "test flag equals zero, ext debug flag true, debug info expected", + in: inTest{test: 0, debug: true}, + out: outTest{debugInfoIncluded: true}, + debugData: debugData{true, true}, }, { - desc: "test flag equals 1, ext debug flag false, debug info expected", - in: inTest{test: 1, debug: false}, - out: outTest{debugInfoIncluded: true}, + desc: "test flag equals 1, ext debug flag false, debug info expected", + in: inTest{test: 1, debug: false}, + out: outTest{debugInfoIncluded: true}, + debugData: debugData{true, true}, }, { - desc: "test flag equals 1, ext debug flag true, debug info expected", - in: inTest{test: 1, debug: true}, - out: outTest{debugInfoIncluded: true}, + desc: "test flag equals 1, ext debug flag true, debug info expected", + in: inTest{test: 1, debug: true}, + out: outTest{debugInfoIncluded: true}, + debugData: debugData{true, true}, }, { - desc: "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected", - in: inTest{test: 2, debug: false}, - out: outTest{debugInfoIncluded: false}, + desc: "test flag not equal to 0 nor 1, ext debug flag false, no debug info expected", + in: inTest{test: 2, debug: false}, + out: outTest{debugInfoIncluded: false}, + debugData: debugData{true, true}, }, { - desc: "test flag not equal to 0 nor 1, ext debug flag true, debug info expected", - in: inTest{test: -1, debug: true}, - out: outTest{debugInfoIncluded: true}, + desc: "test flag not equal to 0 nor 1, ext debug flag true, debug info expected", + in: inTest{test: -1, debug: true}, + out: outTest{debugInfoIncluded: true}, + debugData: debugData{true, true}, + }, + { + desc: "test account level debug disabled", + in: inTest{test: -1, debug: true}, + out: outTest{debugInfoIncluded: false}, + debugData: debugData{true, false}, + }, + { + desc: "test bidder level debug disabled", + in: inTest{test: -1, debug: true}, + out: outTest{debugInfoIncluded: false}, + debugData: debugData{false, true}, }, } @@ -239,17 +264,25 @@ func TestDebugBehaviour(t *testing.T) { } e := new(exchange) - e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ - openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus), - } + e.cache = &wellBehavedCache{} e.me = &metricsConf.DummyMetricsEngine{} e.gDPR = gdpr.AlwaysAllow{} e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) e.categoriesFetcher = categoriesFetcher + ctx := context.Background() + // Run tests for _, test := range testCases { + + e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ + openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: test.debugData.bidderLevelDebugAllowed}), + } + + //request level debug key + ctx = context.WithValue(ctx, DebugContextKey, test.in.debug) + bidRequest.Test = test.in.test if test.in.debug { @@ -260,13 +293,13 @@ func TestDebugBehaviour(t *testing.T) { auctionRequest := AuctionRequest{ BidRequest: bidRequest, - Account: config.Account{}, + Account: config.Account{DebugAllow: test.debugData.accountLevelDebugAllowed}, UserSyncs: &emptyUsersync{}, StartTime: time.Now(), } // Run test - outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, nil) + outBidResponse, err := e.HoldAuction(ctx, auctionRequest, nil) // Assert no HoldAuction error assert.NoErrorf(t, err, "%s. ex.HoldAuction returned an error: %v \n", test.desc, err) @@ -292,8 +325,132 @@ func TestDebugBehaviour(t *testing.T) { if test.in.debug { diffJson(t, test.desc, bidRequest.Ext, actualExt.Debug.ResolvedRequest.Ext) } + } else if !test.debugData.bidderLevelDebugAllowed && test.debugData.accountLevelDebugAllowed { + assert.Equal(t, len(actualExt.Debug.HttpCalls), 0, "%s. ext.debug.httpcalls array should not be empty", "With bidder level debug disable option http calls should be empty") + + } else { + assert.Nil(t, actualExt.Debug, "%s. ext.debug.httpcalls array should not be empty", "With bidder level debug disable option http calls should be empty") + } + } +} + +func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) { + + type testCase struct { + bidder1DebugEnabled bool + bidder2DebugEnabled bool + } + + testCases := []testCase{ + { + bidder1DebugEnabled: true, bidder2DebugEnabled: true, + }, + { + bidder1DebugEnabled: true, bidder2DebugEnabled: false, + }, + { + bidder1DebugEnabled: false, bidder2DebugEnabled: true, + }, + { + bidder1DebugEnabled: false, bidder2DebugEnabled: false, + }, + } + + // Set up test + noBidServer := func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + } + server := httptest.NewServer(http.HandlerFunc(noBidServer)) + defer server.Close() + + categoriesFetcher, err := newCategoryFetcher("./test/category-mapping") + if err != nil { + t.Errorf("Failed to create a category Fetcher: %v", err) + } + + bidderImpl := &goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + Body: []byte("{\"key\":\"val\"}"), + Headers: http.Header{}, + }, + bidResponse: &adapters.BidderResponse{}, + } + + e := new(exchange) + e.cache = &wellBehavedCache{} + e.me = &metricsConf.DummyMetricsEngine{} + e.gDPR = gdpr.AlwaysAllow{} + e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) + e.categoriesFetcher = categoriesFetcher + + debugLog := DebugLog{} + + for _, testCase := range testCases { + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{{ + ID: "some-impression-id", + Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, + Ext: json.RawMessage(`{"telaria": {"placementId": 1}, "appnexus": {"placementid": 2}}`), + }}, + Site: &openrtb.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, + Device: &openrtb.Device{UA: "curl/7.54.0", IP: "::1"}, + AT: 1, + TMax: 500, + } + + bidRequest.Ext = json.RawMessage(`{"prebid":{"debug":true}}`) + + auctionRequest := AuctionRequest{ + BidRequest: bidRequest, + Account: config.Account{DebugAllow: true}, + UserSyncs: &emptyUsersync{}, + StartTime: time.Now(), + } + + e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ + openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder1DebugEnabled}), + openrtb_ext.BidderTelaria: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, &adapters.DebugInfo{Allow: testCase.bidder2DebugEnabled}), + } + // Run test + outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog) + // Assert no HoldAuction err + assert.NoErrorf(t, err, "ex.HoldAuction returned an err") + assert.NotNilf(t, outBidResponse.Ext, "outBidResponse.Ext should not be nil") + + actualExt := &openrtb_ext.ExtBidResponse{} + err = json.Unmarshal(outBidResponse.Ext, actualExt) + assert.NoErrorf(t, err, "JSON field unmarshaling err. ") + + assert.NotEmpty(t, actualExt.Prebid, "ext.prebid should not be empty") + assert.NotEmpty(t, actualExt.Prebid.AuctionTimestamp, "ext.prebid.auctiontimestamp should not be empty when AuctionRequest.StartTime is set") + assert.Equal(t, auctionRequest.StartTime.UnixNano()/1e+6, actualExt.Prebid.AuctionTimestamp, "ext.prebid.auctiontimestamp has incorrect value") + + assert.NotNilf(t, actualExt, "ext.debug field is expected to be included in this outBidResponse.Ext and not be nil") + + // Assert "Debug fields + if testCase.bidder1DebugEnabled { + assert.Equal(t, server.URL, actualExt.Debug.HttpCalls["appnexus"][0].Uri, "Url for bidder with debug enabled is incorrect") + assert.NotNilf(t, actualExt.Debug.HttpCalls["appnexus"][0].RequestBody, "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil") + } + if testCase.bidder2DebugEnabled { + assert.Equal(t, server.URL, actualExt.Debug.HttpCalls["telaria"][0].Uri, "Url for bidder with debug enabled is incorrect") + assert.NotNilf(t, actualExt.Debug.HttpCalls["telaria"][0].RequestBody, "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil") + } + if !testCase.bidder1DebugEnabled { + assert.Nil(t, actualExt.Debug.HttpCalls["appnexus"], "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil") + } + if !testCase.bidder2DebugEnabled { + assert.Nil(t, actualExt.Debug.HttpCalls["telaria"], "ext.debug.resolvedrequest field is expected to be included in this outBidResponse.Ext and not be nil") + } + + if testCase.bidder1DebugEnabled && testCase.bidder2DebugEnabled { + assert.Equal(t, 2, len(actualExt.Debug.HttpCalls), "With bidder level debug enable option for both bidders http calls should have 2 elements") } } + } func TestReturnCreativeEndToEnd(t *testing.T) { @@ -434,7 +591,7 @@ func TestReturnCreativeEndToEnd(t *testing.T) { e := new(exchange) e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ - openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus), + openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil), } e.cache = &wellBehavedCache{} e.me = &metricsConf.DummyMetricsEngine{} @@ -465,7 +622,8 @@ func TestReturnCreativeEndToEnd(t *testing.T) { } // Run test - outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, nil) + debugLog := DebugLog{} + outBidResponse, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog) // Assert return error, if any if testGroup.expectError { @@ -716,7 +874,7 @@ func TestBidReturnsCreative(t *testing.T) { } e := new(exchange) e.adapterMap = map[openrtb_ext.BidderName]adaptedBidder{ - openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus), + openrtb_ext.BidderAppnexus: adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil), } e.cache = &wellBehavedCache{} e.me = &metricsConf.DummyMetricsEngine{} @@ -1059,8 +1217,9 @@ func TestRaceIntegration(t *testing.T) { UserSyncs: &emptyUsersync{}, } + debugLog := DebugLog{} ex := NewExchange(adapters, &wellBehavedCache{}, cfg, &metricsConf.DummyMetricsEngine{}, biddersInfo, gdpr.AlwaysAllow{}, currencyConverter, &nilCategoryFetcher{}).(*exchange) - _, err := ex.HoldAuction(context.Background(), auctionRequest, nil) + _, err := ex.HoldAuction(context.Background(), auctionRequest, &debugLog) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -1279,8 +1438,8 @@ func TestPanicRecoveryHighLevel(t *testing.T) { Account: config.Account{}, UserSyncs: &emptyUsersync{}, } - - _, err := e.HoldAuction(context.Background(), auctionRequest, nil) + debugLog := DebugLog{} + _, err := e.HoldAuction(context.Background(), auctionRequest, &debugLog) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) } @@ -1406,14 +1565,17 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { Account: config.Account{ ID: "testaccount", EventsEnabled: spec.EventsEnabled, + DebugAllow: true, }, UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs), } if spec.StartTime > 0 { auctionRequest.StartTime = time.Unix(0, spec.StartTime*1e+6) } + ctx := context.Background() + ctx = context.WithValue(ctx, DebugContextKey, true) - bid, err := ex.HoldAuction(context.Background(), auctionRequest, debugLog) + bid, err := ex.HoldAuction(ctx, auctionRequest, debugLog) responseTimes := extractResponseTimes(t, filename, bid) for _, bidderName := range biddersInAuction { if _, ok := responseTimes[bidderName]; !ok { @@ -2622,7 +2784,7 @@ type validatingBidder struct { mockResponses map[string]bidderResponse } -func (b *validatingBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (seatBid *pbsOrtbSeatBid, errs []error) { +func (b *validatingBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (seatBid *pbsOrtbSeatBid, errs []error) { if expectedRequest, ok := b.expectations[string(name)]; ok { if expectedRequest != nil { if expectedRequest.BidAdjustment != bidAdjustment { @@ -2801,7 +2963,7 @@ func (e *mockUsersync) LiveSyncCount() int { type panicingAdapter struct{} -func (panicingAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (posb *pbsOrtbSeatBid, errs []error) { +func (panicingAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (posb *pbsOrtbSeatBid, errs []error) { panic("Panic! Panic! The world is ending!") } diff --git a/exchange/exchangetest/append-bidder-names.json b/exchange/exchangetest/append-bidder-names.json index 1247b9f0261..507aee1a103 100644 --- a/exchange/exchangetest/append-bidder-names.json +++ b/exchange/exchangetest/append-bidder-names.json @@ -166,9 +166,6 @@ }, "ext": { "debug": { - "httpcalls": { - "appnexus": null - }, "resolvedrequest": { "id": "some-request-id", "imp": [ diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json index 3dd04f666fa..e95b556b7ec 100644 --- a/exchange/exchangetest/debuglog_disabled.json +++ b/exchange/exchangetest/debuglog_disabled.json @@ -175,9 +175,6 @@ }, "ext": { "debug": { - "httpcalls": { - "appnexus": null - }, "resolvedrequest": { "id": "some-request-id", "imp": [ diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json index a70013b4ac7..851bda69097 100644 --- a/exchange/exchangetest/debuglog_enabled.json +++ b/exchange/exchangetest/debuglog_enabled.json @@ -175,9 +175,6 @@ }, "ext": { "debug": { - "httpcalls": { - "appnexus": null - }, "resolvedrequest": { "id": "some-request-id", "imp": [ diff --git a/exchange/legacy.go b/exchange/legacy.go index dc81e7a1182..b4845b76c69 100644 --- a/exchange/legacy.go +++ b/exchange/legacy.go @@ -33,7 +33,7 @@ type adaptedAdapter struct { // // This is not ideal. OpenRTB provides a superset of the legacy data structures. // For requests which use those features, the best we can do is respond with "no bid". -func (bidder *adaptedAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) { +func (bidder *adaptedAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) { legacyRequest, legacyBidder, errs := bidder.toLegacyAdapterInputs(request, name) if legacyRequest == nil || legacyBidder == nil { return nil, errs diff --git a/exchange/legacy_test.go b/exchange/legacy_test.go index fcc8b09fdc8..2cef4feae40 100644 --- a/exchange/legacy_test.go +++ b/exchange/legacy_test.go @@ -61,7 +61,7 @@ func TestSiteVideo(t *testing.T) { exchangeBidder := adaptLegacyAdapter(&mockAdapter) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if len(errs) > 0 { t.Errorf("Unexpected error requesting bids: %v", errs) } @@ -95,7 +95,7 @@ func TestAppBanner(t *testing.T) { exchangeBidder := adaptLegacyAdapter(&mockAdapter) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if len(errs) > 0 { t.Errorf("Unexpected error requesting bids: %v", errs) } @@ -141,7 +141,7 @@ func TestBidTransforms(t *testing.T) { exchangeBidder := adaptLegacyAdapter(&mockAdapter) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - seatBid, errs := exchangeBidder.requestBid(context.Background(), newAppOrtbRequest(), openrtb_ext.BidderRubicon, bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + seatBid, errs := exchangeBidder.requestBid(context.Background(), newAppOrtbRequest(), openrtb_ext.BidderRubicon, bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if len(errs) != 1 { t.Fatalf("Bad error count. Expected 1, got %d", len(errs)) } @@ -290,7 +290,7 @@ func TestErrorResponse(t *testing.T) { exchangeBidder := adaptLegacyAdapter(&mockAdapter) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + _, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderRubicon, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if len(errs) != 1 { t.Fatalf("Bad error count. Expected 1, got %d", len(errs)) } @@ -329,7 +329,7 @@ func TestWithTargeting(t *testing.T) { } exchangeBidder := adaptLegacyAdapter(&mockAdapter) currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - bid, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderAudienceNetwork, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}) + bid, errs := exchangeBidder.requestBid(context.Background(), ortbRequest, openrtb_ext.BidderAudienceNetwork, 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true) if len(errs) != 0 { t.Fatalf("This should not produce errors. Got %v", errs) } diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 160d7465ff2..2a77c4f7517 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -115,7 +115,8 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op UserSyncs: &emptyUsersync{}, } - bidResp, err := ex.HoldAuction(context.Background(), auctionRequest, nil) + debugLog := DebugLog{} + bidResp, err := ex.HoldAuction(context.Background(), auctionRequest, &debugLog) if err != nil { t.Fatalf("Unexpected errors running auction: %v", err) @@ -141,7 +142,7 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb.Bid, mockServerU adapterMap[bidder] = adaptBidder(&mockTargetingBidder{ mockServerURL: mockServerURL, bids: bids, - }, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus) + }, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil) } return adapterMap } From ef06fac6494f7e91f9d1d90ab07f02f8499b5efc Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Thu, 28 Jan 2021 15:28:17 -0500 Subject: [PATCH 312/318] Always use fallback GVL for TCF1 (#1657) * Update TCF2 GVL subdomain and always use fallback GVL for TCF1 * Add config test coverage for invalid TCF1 FetchGVL and AMP Exception * Delete obselete test --- config/config.go | 4 +- config/config_test.go | 46 ++++- gdpr/vendorlist-fetching.go | 19 +- gdpr/vendorlist-fetching_test.go | 339 ++----------------------------- 4 files changed, 60 insertions(+), 348 deletions(-) diff --git a/config/config.go b/config/config.go index ea5de90d28a..f040040bf64 100755 --- a/config/config.go +++ b/config/config.go @@ -217,7 +217,7 @@ func (cfg *GDPR) validate(errs []error) []error { errs = append(errs, fmt.Errorf("gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)")) } if cfg.TCF1.FetchGVL == true { - glog.Warning("gdpr.tcf1.fetch_gvl is deprecated and will be removed in a future version, at which point TCF1 will always use the fallback GVL") + errs = append(errs, fmt.Errorf("gdpr.tcf1.fetch_gvl has been discontinued and must be removed from your config. TCF1 will always use the fallback GVL going forward")) } return errs } @@ -910,7 +910,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("gdpr.timeouts_ms.init_vendorlist_fetches", 0) v.SetDefault("gdpr.timeouts_ms.active_vendorlist_fetch", 0) v.SetDefault("gdpr.non_standard_publishers", []string{""}) - v.SetDefault("gdpr.tcf1.fetch_gvl", true) + v.SetDefault("gdpr.tcf1.fetch_gvl", false) v.SetDefault("gdpr.tcf1.fallback_gvl_path", "./static/tcf1/fallback_gvl.json") v.SetDefault("gdpr.tcf2.enabled", true) v.SetDefault("gdpr.tcf2.purpose1.enabled", true) diff --git a/config/config_test.go b/config/config_test.go index 57631395fb8..7ff5f0fafa1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -527,12 +527,6 @@ func TestNegativeRequestSize(t *testing.T) { assertOneError(t, cfg.validate(), "cfg.max_request_size must be >= 0. Got -1") } -func TestNegativeVendorID(t *testing.T) { - cfg := newDefaultConfig(t) - cfg.GDPR.HostVendorID = -1 - assertOneError(t, cfg.validate(), "gdpr.host_vendor_id must be in the range [0, 65535]. Got -1") -} - func TestNegativePrometheusTimeout(t *testing.T) { cfg := newDefaultConfig(t) cfg.Metrics.Prometheus.Port = 8001 @@ -540,10 +534,44 @@ func TestNegativePrometheusTimeout(t *testing.T) { assertOneError(t, cfg.validate(), "metrics.prometheus.timeout_ms must be positive if metrics.prometheus.port is defined. Got timeout=0 and port=8001") } -func TestOverflowedVendorID(t *testing.T) { +func TestInvalidHostVendorID(t *testing.T) { + tests := []struct { + description string + vendorID int + wantErrorMsg string + }{ + { + description: "Negative GDPR.HostVendorID", + vendorID: -1, + wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got -1", + }, + { + description: "Overflowed GDPR.HostVendorID", + vendorID: (0xffff) + 1, + wantErrorMsg: "gdpr.host_vendor_id must be in the range [0, 65535]. Got 65536", + }, + } + + for _, tt := range tests { + cfg := newDefaultConfig(t) + cfg.GDPR.HostVendorID = tt.vendorID + errs := cfg.validate() + + assert.Equal(t, 1, len(errs), tt.description) + assert.EqualError(t, errs[0], tt.wantErrorMsg, tt.description) + } +} + +func TestInvalidFetchGVL(t *testing.T) { + cfg := newDefaultConfig(t) + cfg.GDPR.TCF1.FetchGVL = true + assertOneError(t, cfg.validate(), "gdpr.tcf1.fetch_gvl has been discontinued and must be removed from your config. TCF1 will always use the fallback GVL going forward") +} + +func TestInvalidAMPException(t *testing.T) { cfg := newDefaultConfig(t) - cfg.GDPR.HostVendorID = (0xffff) + 1 - assertOneError(t, cfg.validate(), "gdpr.host_vendor_id must be in the range [0, 65535]. Got 65536") + cfg.GDPR.AMPException = true + assertOneError(t, cfg.validate(), "gdpr.amp_exception has been discontinued and must be removed from your config. If you need to disable GDPR for AMP, you may do so per-account (gdpr.integration_enabled.amp) or at the host level for the default account (account_defaults.gdpr.integration_enabled.amp)") } func TestNegativeCurrencyConverterFetchInterval(t *testing.T) { diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index de6da7b4bfb..95a17109c90 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -28,22 +28,21 @@ type saveVendors func(uint16, api.VendorList) func newVendorListFetcher(initCtx context.Context, cfg config.GDPR, client *http.Client, urlMaker func(uint16, uint8) string, tcfSpecVersion uint8) func(ctx context.Context, id uint16) (vendorlist.VendorList, error) { var fallback api.VendorList - if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) > 0 { - fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath) - } - // If we are not going to try fetching the GVL dynamically, we have a simple fetcher. - if !cfg.TCF1.FetchGVL && tcfSpecVersion == tcf1SpecVersion { - if fallback != nil { - return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { - return fallback, nil - } - } + if tcfSpecVersion == tcf1SpecVersion && len(cfg.TCF1.FallbackGVLPath) == 0 { return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { return nil, makeVendorListNotFoundError(vendorListVersion) } } + if tcfSpecVersion == tcf1SpecVersion { + fallback = loadFallbackGVL(cfg.TCF1.FallbackGVLPath) + + return func(ctx context.Context, vendorListVersion uint16) (vendorlist.VendorList, error) { + return fallback, nil + } + } + cacheSave, cacheLoad := newVendorListCache(fallback) preloadContext, cancel := context.WithTimeout(initCtx, cfg.Timeouts.InitTimeout()) diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 62c6a5f9d09..77f3f29463c 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -28,63 +28,24 @@ func TestTCF1FetcherInitialLoad(t *testing.T) { testCases := []test{ { - description: "Fetch - No Fallback - Vendor List 1", + description: "Fallback - Vendor List 1", setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 1, - }, - expected: vendorList1Expected, - }, - { - description: "Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "Fetch - Fallback - Vendor List 1", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 1, - }, - expected: vendorList1Expected, - }, - { - description: "Fetch - Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "No Fetch - Fallback - Vendor List 1", - setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: true, vendorListVersion: 1, }, expected: vendorListFallbackExpected, }, { - description: "No Fetch - Fallback - Vendor List 2", + description: "Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: true, vendorListVersion: 2, }, expected: vendorListFallbackExpected, }, { - description: "No Fetch - No Fallback - Vendor List 1", + description: "No Fallback - Vendor List 1", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 1, }, @@ -93,9 +54,8 @@ func TestTCF1FetcherInitialLoad(t *testing.T) { }, }, { - description: "No Fetch - No Fallback - Vendor List 2", + description: "No Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 2, }, @@ -125,72 +85,32 @@ func TestTCF2FetcherInitialLoad(t *testing.T) { testCases := []test{ { - description: "Fetch - No Fallback - Vendor List 1", + description: "Fallback - Vendor List 1", setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 1, - }, - expected: vendorList1Expected, - }, - { - description: "Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "Fetch - Fallback - Vendor List 1", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 1, - }, - expected: vendorList1Expected, - }, - { - description: "Fetch - Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "No Fetch - Fallback - Vendor List 1", - setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: true, vendorListVersion: 1, }, expected: vendorList1Expected, }, { - description: "No Fetch - Fallback - Vendor List 2", + description: "Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: true, vendorListVersion: 2, }, expected: vendorList2Expected, }, { - description: "No Fetch - No Fallback - Vendor List 1", + description: "No Fallback - Vendor List 1", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 1, }, expected: vendorList1Expected, }, { - description: "No Fetch - No Fallback - Vendor List 2", + description: "No Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 2, }, @@ -203,65 +123,6 @@ func TestTCF2FetcherInitialLoad(t *testing.T) { } } -func TestTCF1FetcherDynamicLoadListExists(t *testing.T) { - // Loads the first vendor list during initialization by setting the latest vendor list version to 1. - // All other vendor lists will be dynamically loaded. - - server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ - vendorListLatestVersion: 1, - vendorLists: map[int]string{ - 1: tcf1VendorList1, - 2: tcf1VendorList2, - }, - }))) - defer server.Close() - - testCases := []test{ - { - description: "Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "Fetch - Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "No Fetch - Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: false, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorListFallbackExpected, - }, - { - description: "No Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: false, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: testExpected{ - errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", - }, - }, - } - - for _, test := range testCases { - runTest(t, test, tcf1SpecVersion, server) - } -} - func TestTCF2FetcherDynamicLoadListExists(t *testing.T) { // Loads the first vendor list during initialization by setting the latest vendor list version to 1. // All other vendor lists will be dynamically loaded. @@ -278,36 +139,16 @@ func TestTCF2FetcherDynamicLoadListExists(t *testing.T) { testCases := []test{ { - description: "Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "Fetch - Fallback - Vendor List 2", + description: "Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: true, enableTCF1Fallback: true, vendorListVersion: 2, }, expected: vendorList2Expected, }, { - description: "No Fetch - Fallback - Vendor List 2", + description: "No Fallback - Vendor List 2", setup: testSetup{ - enableTCF1Fetch: false, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorList2Expected, - }, - { - description: "No Fetch - No Fallback - Vendor List 2", - setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 2, }, @@ -320,66 +161,6 @@ func TestTCF2FetcherDynamicLoadListExists(t *testing.T) { } } -func TestTCF1FetcherDynamicLoadListDoesntExist(t *testing.T) { - // Loads the first vendor list during initialization by setting the latest vendor list version to 1. - // All other vendor list load attempts will be done dynamically. - - server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ - vendorListLatestVersion: 1, - vendorLists: map[int]string{ - 1: tcf1VendorList1, - }, - }))) - defer server.Close() - - testCases := []test{ - { - description: "Fetch - No Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: testExpected{ - errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", - }, - }, - { - description: "Fetch - Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorListFallbackExpected, - }, - { - description: "No Fetch - Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: false, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: vendorListFallbackExpected, - }, - { - description: "No Fetch - No Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: false, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: testExpected{ - errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", - }, - }, - } - - for _, test := range testCases { - runTest(t, test, 1, server) - } -} - func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) { // Loads the first vendor list during initialization by setting the latest vendor list version to 1. // All other vendor list load attempts will be done dynamically. @@ -395,31 +176,8 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) { testCases := []test{ { - description: "Fetch - No Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: false, - vendorListVersion: 2, - }, - expected: testExpected{ - errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", - }, - }, - { - description: "Fetch - Fallback - Vendor Doesn't Exist", - setup: testSetup{ - enableTCF1Fetch: true, - enableTCF1Fallback: true, - vendorListVersion: 2, - }, - expected: testExpected{ - errorMessage: "gdpr vendor list version 2 does not exist, or has not been loaded yet. Try again in a few minutes", - }, - }, - { - description: "No Fetch - Fallback - Vendor Doesn't Exist", + description: "Fallback - Vendor Doesn't Exist", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: true, vendorListVersion: 2, }, @@ -428,9 +186,8 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) { }, }, { - description: "No Fetch - No Fallback - Vendor Doesn't Exist", + description: "No Fallback - Vendor Doesn't Exist", setup: testSetup{ - enableTCF1Fetch: false, enableTCF1Fallback: false, vendorListVersion: 2, }, @@ -445,38 +202,6 @@ func TestTCF2FetcherDynamicLoadListDoesntExist(t *testing.T) { } } -func TestTCF1FetcherThrottling(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ - vendorListLatestVersion: 1, - vendorLists: map[int]string{ - 1: tcf1MarshalVendorList(tcf1VendorList{ - VendorListVersion: 1, - Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1}}}, - }), - 2: tcf1MarshalVendorList(tcf1VendorList{ - VendorListVersion: 2, - Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1, 2}}}, - }), - 3: tcf1MarshalVendorList(tcf1VendorList{ - VendorListVersion: 3, - Vendors: []tcf1Vendor{{ID: 12, Purposes: []int{1, 2, 3}}}, - }), - }, - }))) - defer server.Close() - - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) - - // Dynamically Load List 2 Successfully - _, errList1 := fetcher(context.Background(), 2) - assert.NoError(t, errList1) - - // Fail To Load List 3 Due To Rate Limiting - // - The request is rate limited after dynamically list 2. - _, errList2 := fetcher(context.Background(), 3) - assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes") -} - func TestTCF2FetcherThrottling(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ vendorListLatestVersion: 1, @@ -509,22 +234,6 @@ func TestTCF2FetcherThrottling(t *testing.T) { assert.EqualError(t, errList2, "gdpr vendor list version 3 does not exist, or has not been loaded yet. Try again in a few minutes") } -func TestTCF1MalformedVendorlist(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ - vendorListLatestVersion: 1, - vendorLists: map[int]string{ - 1: "malformed", - }, - }))) - defer server.Close() - - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) - _, err := fetcher(context.Background(), 1) - - // Fetching should fail since vendor list could not be unmarshalled. - assert.Error(t, err) -} - func TestTCF2MalformedVendorlist(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(mockServer(serverSettings{ vendorListLatestVersion: 1, @@ -541,18 +250,6 @@ func TestTCF2MalformedVendorlist(t *testing.T) { assert.Error(t, err) } -func TestTCF1ServerUrlInvalid(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() - - invalidURLGenerator := func(uint16, uint8) string { return " http://invalid-url-has-leading-whitespace" } - - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), invalidURLGenerator, tcf1SpecVersion) - _, err := fetcher(context.Background(), 1) - - assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") -} - func TestTCF2ServerUrlInvalid(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) server.Close() @@ -565,16 +262,6 @@ func TestTCF2ServerUrlInvalid(t *testing.T) { assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") } -func TestTCF1ServerUnavailable(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - server.Close() - - fetcher := newVendorListFetcher(context.Background(), testConfig(), server.Client(), testURLMaker(server), tcf1SpecVersion) - _, err := fetcher(context.Background(), 1) - - assert.EqualError(t, err, "gdpr vendor list version 1 does not exist, or has not been loaded yet. Try again in a few minutes") -} - func TestTCF2ServerUnavailable(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) server.Close() @@ -740,7 +427,6 @@ type test struct { } type testSetup struct { - enableTCF1Fetch bool enableTCF1Fallback bool vendorListVersion uint16 } @@ -754,7 +440,6 @@ type testExpected struct { func runTest(t *testing.T, test test, tcfSpecVersion uint8, server *httptest.Server) { config := testConfig() - config.TCF1.FetchGVL = test.setup.enableTCF1Fetch if test.setup.enableTCF1Fallback { config.TCF1.FallbackGVLPath = "../static/tcf1/fallback_gvl.json" } From 27d1b8f4e918317f470b96b65e591bd3cbaa0d48 Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Wed, 3 Feb 2021 20:10:45 +0200 Subject: [PATCH 313/318] Adform adapter: digitrust cleanup (#1690) * adform secure endpoint as default setting * digitrust cleanup --- adapters/adform/adform.go | 56 +------------------ adapters/adform/adform_test.go | 17 ++---- .../exemplary/multiformat-impression.json | 2 +- .../exemplary/single-banner-impression.json | 2 +- .../exemplary/single-video-impression.json | 2 +- .../adformtest/supplemental/user-nil.json | 2 +- config/config.go | 2 +- 7 files changed, 11 insertions(+), 72 deletions(-) diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go index 5eeeb132786..e84df5b8cc7 100644 --- a/adapters/adform/adform.go +++ b/adapters/adform/adform.go @@ -44,23 +44,11 @@ type adformRequest struct { adUnits []*adformAdUnit gdprApplies string consent string - digitrust *adformDigitrust currency string eids string url string } -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 { MasterTagId json.Number `json:"mid"` PriceType string `json:"priceType,omitempty"` @@ -216,24 +204,6 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder 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, @@ -247,7 +217,6 @@ func pbsRequestToAdformRequest(a *AdformAdapter, request *pbs.PBSRequest, bidder tid: request.Tid, gdprApplies: gdprApplies, consent: consent, - digitrust: digitrust, currency: defaultCurrency, }, nil } @@ -371,18 +340,9 @@ func (r *adformRequest) buildAdformHeaders(a *AdformAdapter) http.Header { header.Set("Referer", r.referer) } - cookie := make([]string, 0, 2) if 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", fmt.Sprintf("uid=%s;", r.userId)) } - header.Set("Cookie", strings.Join(cookie, ";")) return header } @@ -506,27 +466,14 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro eids := "" 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 eids = encodeEids(extUser.Eids) } } - 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, - } - } - requestCurrency := defaultCurrency if len(request.Cur) != 0 { hasDefaultCurrency := false @@ -552,7 +499,6 @@ func openRtbToAdformRequest(request *openrtb.BidRequest) (*adformRequest, []erro tid: tid, gdprApplies: gdprApplies, consent: consent, - digitrust: digitrust, currency: requestCurrency, eids: eids, url: url, diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index 8ce55a1231c..558453e4030 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -27,7 +27,7 @@ import ( func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderAdform, config.Adapter{ - Endpoint: "http://adx.adform.net/adx"}) + Endpoint: "https://adx.adform.net/adx"}) if buildErr != nil { t.Fatalf("Builder returned unexpected error %v", buildErr) @@ -300,7 +300,7 @@ func preparePrebidRequestBody(requestData aBidInfo, t *testing.T) *bytes.Buffer func TestOpenRTBRequest(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderAdform, config.Adapter{ - Endpoint: "http://adx.adform.net"}) + Endpoint: "https://adx.adform.net"}) if buildErr != nil { t.Fatalf("Builder returned unexpected error %v", buildErr) @@ -525,12 +525,6 @@ func getRegs() openrtb.Regs { } func getUserExt() []byte { - digitrust := openrtb_ext.ExtUserDigiTrust{ - ID: "digitrustId", - KeyV: 1, - Pref: 0, - } - eids := []openrtb_ext.ExtUserEid{ { Source: "test.com", @@ -556,9 +550,8 @@ func getUserExt() []byte { } userExt := openrtb_ext.ExtUser{ - Eids: eids, - Consent: "abc", - DigiTrust: &digitrust, + Eids: eids, + Consent: "abc", } userExtData, err := json.Marshal(userExt) if err == nil { @@ -630,7 +623,7 @@ func assertAdformServerRequest(testData aBidInfo, r *http.Request, isOpenRtb boo if ok, err := equal(testData.referrer, r.Header.Get("Referer"), "Referer"); !ok { return err } - if ok, err := equal(fmt.Sprintf("uid=%s;DigiTrust.v1.identity=eyJpZCI6ImRpZ2l0cnVzdElkIiwidmVyc2lvbiI6MSwia2V5diI6MSwicHJpdmFjeSI6eyJvcHRvdXQiOmZhbHNlfX0", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok { + if ok, err := equal(fmt.Sprintf("uid=%s;", testData.buyerUID), r.Header.Get("Cookie"), "Buyer ID"); !ok { return err } return nil diff --git a/adapters/adform/adformtest/exemplary/multiformat-impression.json b/adapters/adform/adformtest/exemplary/multiformat-impression.json index efd4aca63e2..5b3067ab927 100644 --- a/adapters/adform/adformtest/exemplary/multiformat-impression.json +++ b/adapters/adform/adformtest/exemplary/multiformat-impression.json @@ -35,7 +35,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE&bWlkPTU0MzIxJnJjdXI9VVNE" + "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE&bWlkPTU0MzIxJnJjdXI9VVNE" }, "mockResponse": { "status": 200, diff --git a/adapters/adform/adformtest/exemplary/single-banner-impression.json b/adapters/adform/adformtest/exemplary/single-banner-impression.json index fd7f3cde526..8a5f81c8edb 100644 --- a/adapters/adform/adformtest/exemplary/single-banner-impression.json +++ b/adapters/adform/adformtest/exemplary/single-banner-impression.json @@ -23,7 +23,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE" + "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTEyMzQ1JnJjdXI9VVNE" }, "mockResponse": { "status": 200, diff --git a/adapters/adform/adformtest/exemplary/single-video-impression.json b/adapters/adform/adformtest/exemplary/single-video-impression.json index e22977e6523..383e091b3f7 100644 --- a/adapters/adform/adformtest/exemplary/single-video-impression.json +++ b/adapters/adform/adformtest/exemplary/single-video-impression.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTU0MzIxJnJjdXI9VVNE" + "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=&gdpr_consent=&ip=&rp=4&stid=&bWlkPTU0MzIxJnJjdXI9VVNE" }, "mockResponse": { "status": 200, diff --git a/adapters/adform/adformtest/supplemental/user-nil.json b/adapters/adform/adformtest/supplemental/user-nil.json index 34b857d556a..5f02fe85971 100644 --- a/adapters/adform/adformtest/supplemental/user-nil.json +++ b/adapters/adform/adformtest/supplemental/user-nil.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://adx.adform.net/adx?CC=1&fd=1&gdpr=1&gdpr_consent=abc2&ip=&pt=gross&rp=4&stid=&bWlkPTEmcmN1cj1VU0Q" + "uri": "https://adx.adform.net/adx?CC=1&fd=1&gdpr=1&gdpr_consent=abc2&ip=&pt=gross&rp=4&stid=&bWlkPTEmcmN1cj1VU0Q" }, "mockResponse": { "status": 204 diff --git a/config/config.go b/config/config.go index f040040bf64..67964f0dde3 100755 --- a/config/config.go +++ b/config/config.go @@ -794,7 +794,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb") v.SetDefault("adapters.33across.partner_id", "") v.SetDefault("adapters.acuityads.endpoint", "http://{{.Host}}.admanmedia.com/bid?token={{.AccountID}}") - v.SetDefault("adapters.adform.endpoint", "http://adx.adform.net/adx") + v.SetDefault("adapters.adform.endpoint", "https://adx.adform.net/adx") v.SetDefault("adapters.adgeneration.endpoint", "https://d.socdm.com/adsv/v1") v.SetDefault("adapters.adhese.endpoint", "https://ads-{{.AccountID}}.adhese.com/json") v.SetDefault("adapters.adkernel.endpoint", "http://{{.Host}}/hb?zone={{.ZoneID}}") From 3b93511eb8076a6e47a65f83930c09a425b9b759 Mon Sep 17 00:00:00 2001 From: Vladyslav Laktionov Date: Thu, 4 Feb 2021 08:12:55 +0200 Subject: [PATCH 314/318] New Adapter: DecenterAds (#1669) Co-authored-by: vlad --- adapters/decenterads/decenterads.go | 124 +++++++++++++++++ adapters/decenterads/decenterads_test.go | 18 +++ .../exemplary/simple-banner.json | 128 ++++++++++++++++++ .../exemplary/simple-video.json | 116 ++++++++++++++++ .../exemplary/simple-web-banner.json | 126 +++++++++++++++++ .../decenteradstest/params/race/banner.json | 3 + .../decenteradstest/params/race/video.json | 3 + .../supplemental/bad-imp-ext.json | 41 ++++++ .../supplemental/bad_response.json | 83 ++++++++++++ .../supplemental/bad_status_code.json | 77 +++++++++++ .../supplemental/imp_ext_empty_object.json | 37 +++++ .../supplemental/imp_ext_string.json | 37 +++++ .../supplemental/status-204.json | 78 +++++++++++ .../supplemental/status-404.json | 83 ++++++++++++ adapters/decenterads/params_test.go | 48 +++++++ config/config.go | 2 + exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_decenterads.go | 5 + static/bidder-info/decenterads.yaml | 14 ++ static/bidder-params/decenterads.json | 18 +++ usersync/usersyncers/syncer_test.go | 1 + 22 files changed, 1046 insertions(+) create mode 100644 adapters/decenterads/decenterads.go create mode 100644 adapters/decenterads/decenterads_test.go create mode 100644 adapters/decenterads/decenteradstest/exemplary/simple-banner.json create mode 100644 adapters/decenterads/decenteradstest/exemplary/simple-video.json create mode 100644 adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json create mode 100644 adapters/decenterads/decenteradstest/params/race/banner.json create mode 100644 adapters/decenterads/decenteradstest/params/race/video.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/bad_response.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/bad_status_code.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/imp_ext_string.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/status-204.json create mode 100644 adapters/decenterads/decenteradstest/supplemental/status-404.json create mode 100644 adapters/decenterads/params_test.go create mode 100644 openrtb_ext/imp_decenterads.go create mode 100644 static/bidder-info/decenterads.yaml create mode 100644 static/bidder-params/decenterads.json diff --git a/adapters/decenterads/decenterads.go b/adapters/decenterads/decenterads.go new file mode 100644 index 00000000000..5719bf1e4b3 --- /dev/null +++ b/adapters/decenterads/decenterads.go @@ -0,0 +1,124 @@ +package decenterads + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type adapter struct { + endpoint string +} + +func (a *adapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + impressions := request.Imp + result := make([]*adapters.RequestData, 0, len(impressions)) + errs := make([]error, 0, len(impressions)) + + for _, impression := range impressions { + var impExt map[string]json.RawMessage + if err := json.Unmarshal(impression.Ext, &impExt); err != nil { + errs = append(errs, fmt.Errorf("unable to parse bidder parameers: %s", err)) + continue + } + + bidderExt, bidderExtExists := impExt["bidder"] + if !bidderExtExists || len(bidderExt) == 0 { + errs = append(errs, errors.New("bidder parameters required")) + continue + } + + impression.Ext = bidderExt + request.Imp = []openrtb.Imp{impression} + body, err := json.Marshal(request) + if err != nil { + errs = append(errs, err) + continue + } + result = append(result, &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: body, + Headers: headers, + }) + } + + request.Imp = impressions + return result, errs +} + +func (a *adapter) MakeBids(request *openrtb.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + switch responseData.StatusCode { + case http.StatusNoContent: + return nil, nil + case http.StatusBadRequest: + return nil, []error{&errortypes.BadInput{ + Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode), + }} + case http.StatusOK: + break + default: + return nil, []error{&errortypes.BadServerResponse{ + Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode), + }} + } + + var bidResponse openrtb.BidResponse + err := json.Unmarshal(responseData.Body, &bidResponse) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + + response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + + for _, seatBid := range bidResponse.SeatBid { + for i := range seatBid.Bid { + response.Bids = append(response.Bids, &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: getMediaTypeForImp(seatBid.Bid[i].ImpID, request.Imp), + }) + } + } + + return response, errs +} + +func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType { + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + return openrtb_ext.BidTypeVideo + } else if imp.Native != nil { + return openrtb_ext.BidTypeNative + } else if imp.Audio != nil { + return openrtb_ext.BidTypeAudio + } + } + } + return openrtb_ext.BidTypeBanner +} + +// Builder builds a new instance of the DecenterAds adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) { + bidder := &adapter{ + endpoint: config.Endpoint, + } + return bidder, nil +} diff --git a/adapters/decenterads/decenterads_test.go b/adapters/decenterads/decenterads_test.go new file mode 100644 index 00000000000..ca86e89187c --- /dev/null +++ b/adapters/decenterads/decenterads_test.go @@ -0,0 +1,18 @@ +package decenterads + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderDecenterAds, config.Adapter{ + Endpoint: "http://example.com/?c=o&m=ortb"}) + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + adapterstest.RunJSONBidderTest(t, "decenteradstest", bidder) +} diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-banner.json b/adapters/decenterads/decenteradstest/exemplary/simple-banner.json new file mode 100644 index 00000000000..fd2c09d01f7 --- /dev/null +++ b/adapters/decenterads/decenteradstest/exemplary/simple-banner.json @@ -0,0 +1,128 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "3" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } +}, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "3" + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-video.json b/adapters/decenterads/decenteradstest/exemplary/simple-video.json new file mode 100644 index 00000000000..b85d302b662 --- /dev/null +++ b/adapters/decenterads/decenteradstest/exemplary/simple-video.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "ext": { + "bidder": { + "placementId": "3" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + }, + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1024, + "h": 576 + }, + "ext": { + "placementId": "3" + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "decenterads" + } + ], + "cur": "USD" + } + } + } + ], + + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "00:01:00", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json b/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json new file mode 100644 index 00000000000..8f8aa0e94b1 --- /dev/null +++ b/adapters/decenterads/decenteradstest/exemplary/simple-web-banner.json @@ -0,0 +1,126 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "3" + } + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "3" + } + } + ], + "site": { + "id": "1", + "domain": "test.com" + }, + "device": { + "ip": "123.123.123.123" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ], + "seat": "decenterads" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.27543, + "adm": "", + "cid": "test_cid", + "crid": "test_crid", + "dealid": "test_dealid", + "w": 468, + "h": 60, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/decenterads/decenteradstest/params/race/banner.json b/adapters/decenterads/decenteradstest/params/race/banner.json new file mode 100644 index 00000000000..dbdac1ad995 --- /dev/null +++ b/adapters/decenterads/decenteradstest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "TagID": "6" +} \ No newline at end of file diff --git a/adapters/decenterads/decenteradstest/params/race/video.json b/adapters/decenterads/decenteradstest/params/race/video.json new file mode 100644 index 00000000000..6e2e0b3803b --- /dev/null +++ b/adapters/decenterads/decenteradstest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "TagID": "7" +} \ No newline at end of file diff --git a/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json b/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json new file mode 100644 index 00000000000..83455ca70b6 --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/bad-imp-ext.json @@ -0,0 +1,41 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "decenterads": { + "placementId": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "bidder parameters required", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/bad_response.json b/adapters/decenterads/decenteradstest/supplemental/bad_response.json new file mode 100644 index 00000000000..9d9d977b14a --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/bad_response.json @@ -0,0 +1,83 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "1" + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 200, + "body": "" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb.BidResponse", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json b/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json new file mode 100644 index 00000000000..4a81da360e4 --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/bad_status_code.json @@ -0,0 +1,77 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": {} + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "1" + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": {} + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 400", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json b/adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json new file mode 100644 index 00000000000..db4053f2e1f --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/imp_ext_empty_object.json @@ -0,0 +1,37 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": {} + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "bidder parameters required", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/imp_ext_string.json b/adapters/decenterads/decenteradstest/supplemental/imp_ext_string.json new file mode 100644 index 00000000000..db938d6a652 --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/imp_ext_string.json @@ -0,0 +1,37 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": "" + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "zxcjbzxmc-zxcbmz-zxbcz-zxczx" + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "unable to parse bidder parameers: json: cannot unmarshal string into Go value of type map[string]json.RawMessage", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/status-204.json b/adapters/decenterads/decenteradstest/supplemental/status-204.json new file mode 100644 index 00000000000..3eff0fdcd8d --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/status-204.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "1" + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "expectedBidResponses": [], + "mockResponse": { + "status": 204, + "body": {} + } + } + ] +} diff --git a/adapters/decenterads/decenteradstest/supplemental/status-404.json b/adapters/decenterads/decenteradstest/supplemental/status-404.json new file mode 100644 index 00000000000..a162dd897ea --- /dev/null +++ b/adapters/decenterads/decenteradstest/supplemental/status-404.json @@ -0,0 +1,83 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placementId": "1" + } + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://example.com/?c=o&m=ortb", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "placementId": "1" + } + } + ], + "app": { + "id": "1", + "bundle": "com.wls.testwlsapplication" + }, + "device": { + "ip": "123.123.123.123", + "ifa": "sdjfksdf-dfsds-dsdg-dsgg" + } + } + }, + "mockResponse": { + "status": 404, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected status code: 404", + "comparison": "literal" + } + ] +} diff --git a/adapters/decenterads/params_test.go b/adapters/decenterads/params_test.go new file mode 100644 index 00000000000..3d3708be789 --- /dev/null +++ b/adapters/decenterads/params_test.go @@ -0,0 +1,48 @@ +package decenterads + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// TestValidParams makes sure that the decenterads schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderDecenterAds, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected decenterads params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the decenterads schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderDecenterAds, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placementId": "11"}`, +} + +var invalidParams = []string{ + `{"id": "456"}`, + `{"placementid": "3456"}`, + `{"placement_id": 346}`, + `{"placementID": ""}`, + `{"placementId": 234}`, +} diff --git a/config/config.go b/config/config.go index 67964f0dde3..ae1f62e90b4 100755 --- a/config/config.go +++ b/config/config.go @@ -590,6 +590,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderConversant, "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dconversant%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderCpmstar, "https://server.cpmstar.com/usersync.aspx?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dcpmstar%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDatablocks, "https://sync.v5prebid.datablocks.net/s2ssync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddatablocks%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D") + // openrtb_ext.BidderDecenterAds doesn't have a good default. // openrtb_ext.BidderDMX doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderDeepintent, "https://match.deepintent.com/usersync/136?id=unk&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ddeepintent%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BUID%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEmxDigital, "https://cs.emxdgt.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Demx_digital%26uid%3D%24UID") @@ -828,6 +829,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25") v.SetDefault("adapters.cpmstar.endpoint", "https://server.cpmstar.com/openrtbbidrq.aspx") v.SetDefault("adapters.datablocks.endpoint", "http://{{.Host}}/openrtb2?sid={{.SourceId}}") + v.SetDefault("adapters.decenterads.endpoint", "http://supply.decenterads.com/?c=o&m=rtb") v.SetDefault("adapters.deepintent.endpoint", "https://prebid.deepintent.com/prebid") v.SetDefault("adapters.dmx.endpoint", "https://dmx-direct.districtm.io/b/v2") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 8da57d6ec59..f05a4d817fe 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -35,6 +35,7 @@ import ( "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/cpmstar" "github.com/prebid/prebid-server/adapters/datablocks" + "github.com/prebid/prebid-server/adapters/decenterads" "github.com/prebid/prebid-server/adapters/deepintent" "github.com/prebid/prebid-server/adapters/dmx" "github.com/prebid/prebid-server/adapters/emx_digital" @@ -136,6 +137,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderConversant: conversant.Builder, openrtb_ext.BidderCpmstar: cpmstar.Builder, openrtb_ext.BidderDatablocks: datablocks.Builder, + openrtb_ext.BidderDecenterAds: decenterads.Builder, openrtb_ext.BidderDeepintent: deepintent.Builder, openrtb_ext.BidderDmx: dmx.Builder, openrtb_ext.BidderEmxDigital: emx_digital.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index c0839632c7e..576b1d1ac6f 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -74,6 +74,7 @@ const ( BidderCpmstar BidderName = "cpmstar" BidderDatablocks BidderName = "datablocks" BidderDmx BidderName = "dmx" + BidderDecenterAds BidderName = "decenterads" BidderDeepintent BidderName = "deepintent" BidderEmxDigital BidderName = "emx_digital" BidderEngageBDR BidderName = "engagebdr" @@ -173,6 +174,7 @@ func CoreBidderNames() []BidderName { BidderConversant, BidderCpmstar, BidderDatablocks, + BidderDecenterAds, BidderDeepintent, BidderDmx, BidderEmxDigital, diff --git a/openrtb_ext/imp_decenterads.go b/openrtb_ext/imp_decenterads.go new file mode 100644 index 00000000000..101b85c13ad --- /dev/null +++ b/openrtb_ext/imp_decenterads.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpDecenterAds struct { + PlacementID string `json:"placementId"` +} diff --git a/static/bidder-info/decenterads.yaml b/static/bidder-info/decenterads.yaml new file mode 100644 index 00000000000..c9b349cd78a --- /dev/null +++ b/static/bidder-info/decenterads.yaml @@ -0,0 +1,14 @@ +maintainer: + email: "support@decenterads.com" +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/decenterads.json b/static/bidder-params/decenterads.json new file mode 100644 index 00000000000..970e99038a0 --- /dev/null +++ b/static/bidder-params/decenterads.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DecenterAds Adapter Params", + "description": "A schema which validates params accepted by the DecenterAds adapter", + "type": "object", + "properties": { + "placementId": { + "type": "string", + "minLength": 1, + "description": "An ID which identifies the DecenterAds placement" + }, + "customParams": { + "type": "object", + "description": "User-defined targeting key-value pairs." + } + }, + "required" : [ "placementId" ] +} diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index bca8623d98b..73f0e8861c5 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -101,6 +101,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderAdot: true, openrtb_ext.BidderAdprime: true, openrtb_ext.BidderApplogy: true, + openrtb_ext.BidderDecenterAds: true, openrtb_ext.BidderInMobi: true, openrtb_ext.BidderKidoz: true, openrtb_ext.BidderKubient: true, From 3b9f61aec86aed9a69316c45310664cf17ef0290 Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Thu, 4 Feb 2021 08:39:06 -0500 Subject: [PATCH 315/318] Handle empty consent string during cookie sync and setuid (#1671) * Handle empty consent string during cookie sync and setuid * Remove todo comment * Make auction test table driven and convert GDPR impl normalize method to pass by value * Moved legacy auction endpoint signal parsing into its own method and removed unnecessary test cases * Fix SignalParse method to return nil for error when raw signal is empty and other PR feedback --- endpoints/auction.go | 23 +++---- endpoints/auction_test.go | 74 ++++++++++++++------ endpoints/cookie_sync.go | 5 +- endpoints/cookie_sync_test.go | 4 +- endpoints/setuid.go | 58 +++++++++------- endpoints/setuid_test.go | 17 ++--- exchange/utils_test.go | 4 +- gdpr/gdpr.go | 24 ++++++- gdpr/gdpr_test.go | 52 ++++++++++++++ gdpr/impl.go | 54 +++++++++------ gdpr/impl_test.go | 123 ++++++++++++++++++++++++---------- 11 files changed, 306 insertions(+), 132 deletions(-) diff --git a/endpoints/auction.go b/endpoints/auction.go index 0604c224458..92e9769d59e 100644 --- a/endpoints/auction.go +++ b/endpoints/auction.go @@ -189,21 +189,16 @@ func (a *auction) recoverSafely(inner func(*pbs.PBSBidder, metrics.AdapterLabels } func (a *auction) shouldUsersync(ctx context.Context, bidder openrtb_ext.BidderName, gdprPrivacyPolicy gdprPrivacy.Policy) bool { - switch gdprPrivacyPolicy.Signal { - case "0": - return true - case "1": - if gdprPrivacyPolicy.Consent == "" { - return false - } - fallthrough - default: - if canSync, err := a.gdprPerms.HostCookiesAllowed(ctx, gdprPrivacyPolicy.Consent); !canSync || err != nil { - return false - } - canSync, err := a.gdprPerms.BidderSyncAllowed(ctx, bidder, gdprPrivacyPolicy.Consent) - return canSync && err == nil + gdprSignal := gdpr.SignalAmbiguous + if signal, err := gdpr.SignalParse(gdprPrivacyPolicy.Signal); err != nil { + gdprSignal = signal + } + + if canSync, err := a.gdprPerms.HostCookiesAllowed(ctx, gdprSignal, gdprPrivacyPolicy.Consent); err != nil || !canSync { + return false } + canSync, err := a.gdprPerms.BidderSyncAllowed(ctx, bidder, gdprSignal, gdprPrivacyPolicy.Consent) + return canSync && err == nil } // cache video bids only for Web diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go index db26a84b949..ed9a526d760 100644 --- a/endpoints/auction_test.go +++ b/endpoints/auction_test.go @@ -22,6 +22,8 @@ import ( gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr" "github.com/prebid/prebid-server/usersync/usersyncers" "github.com/spf13/viper" + + "github.com/stretchr/testify/assert" ) func TestSortBidsAndAddKeywordsForMobile(t *testing.T) { @@ -376,32 +378,64 @@ func TestCacheVideoOnly(t *testing.T) { } func TestShouldUsersync(t *testing.T) { - doTest := func(gdprApplies string, consent string, allowBidderSync bool, allowHostCookies bool, expectAllow bool) { - t.Helper() + tests := []struct { + description string + signal string + allowHostCookies bool + allowBidderSync bool + wantAllow bool + }{ + { + description: "Don't sync - GDPR on, host cookies disallows and bidder sync disallows", + signal: "1", + allowHostCookies: false, + allowBidderSync: false, + wantAllow: false, + }, + { + description: "Don't sync - GDPR on, host cookies disallows and bidder sync allows", + signal: "1", + allowHostCookies: false, + allowBidderSync: true, + wantAllow: false, + }, + { + description: "Don't sync - GDPR on, host cookies allows and bidder sync disallows", + signal: "1", + allowHostCookies: true, + allowBidderSync: false, + wantAllow: false, + }, + { + description: "Sync - GDPR on, host cookies allows and bidder sync allows", + signal: "1", + allowHostCookies: true, + allowBidderSync: true, + wantAllow: true, + }, + { + description: "Don't sync - invalid GDPR signal, host cookies disallows and bidder sync disallows", + signal: "2", + allowHostCookies: false, + allowBidderSync: false, + wantAllow: false, + }, + } + + for _, tt := range tests { deps := auction{ - cfg: nil, - syncers: nil, gdprPerms: &auctionMockPermissions{ - allowBidderSync: allowBidderSync, - allowHostCookies: allowHostCookies, + allowBidderSync: tt.allowBidderSync, + allowHostCookies: tt.allowHostCookies, }, - metricsEngine: nil, } gdprPrivacyPolicy := gdprPolicy.Policy{ - Signal: gdprApplies, - Consent: consent, + Signal: tt.signal, } - allowSyncs := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, gdprPrivacyPolicy) - if allowSyncs != expectAllow { - t.Errorf("Expected syncs: %t, allowed syncs: %t", expectAllow, allowSyncs) - } + allow := deps.shouldUsersync(context.Background(), openrtb_ext.BidderAdform, gdprPrivacyPolicy) + assert.Equal(t, tt.wantAllow, allow, tt.description) } - doTest("0", "", false, false, true) - doTest("1", "", true, true, false) - doTest("1", "a", true, false, false) - doTest("1", "a", false, true, false) - doTest("1", "a", true, true, true) } type auctionMockPermissions struct { @@ -412,11 +446,11 @@ type auctionMockPermissions struct { allowID bool } -func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (m *auctionMockPermissions) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) { return m.allowHostCookies, nil } -func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (m *auctionMockPermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) { return m.allowBidderSync, nil } diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 21876af9efd..bf3935f0535 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -248,13 +248,14 @@ func (req *cookieSyncRequest) filterForGDPR(permissions gdpr.Permissions) { return } - if allowSync, err := permissions.HostCookiesAllowed(context.Background(), req.Consent); err != nil || !allowSync { + // At this point we know the gdpr signal is Yes because the upstream call to parseRequest already denormalized the signal if it was ambiguous + if allowSync, err := permissions.HostCookiesAllowed(context.Background(), gdpr.SignalYes, req.Consent); err != nil || !allowSync { req.Bidders = nil return } for i := 0; i < len(req.Bidders); i++ { - if allowSync, err := permissions.BidderSyncAllowed(context.Background(), openrtb_ext.BidderName(req.Bidders[i]), req.Consent); err != nil || !allowSync { + if allowSync, err := permissions.BidderSyncAllowed(context.Background(), openrtb_ext.BidderName(req.Bidders[i]), gdpr.SignalYes, req.Consent); err != nil || !allowSync { req.Bidders = append(req.Bidders[:i], req.Bidders[i+1:]...) i-- } diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index c790fcd9d74..a6352387786 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -245,11 +245,11 @@ type gdprPerms struct { allowedBidders map[openrtb_ext.BidderName]usersync.Usersyncer } -func (g *gdprPerms) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (g *gdprPerms) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) { return g.allowHost, nil } -func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (g *gdprPerms) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) { _, ok := g.allowedBidders[bidder] return ok, nil } diff --git a/endpoints/setuid.go b/endpoints/setuid.go index caa3ae1766d..4bff02acf37 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -143,32 +143,38 @@ func checkChromeBrowserVersion(ua string, index int, chromeStrLength int) bool { return result } -func preventSyncsGDPR(gdprEnabled string, gdprConsent string, perms gdpr.Permissions) (bool, int, string) { - switch gdprEnabled { - case "0": - return false, 0, "" - case "1": - if gdprConsent == "" { - return true, http.StatusBadRequest, "gdpr_consent is required when gdpr=1" - } - fallthrough - case "": - if allowed, err := perms.HostCookiesAllowed(context.Background(), gdprConsent); err != nil { - if _, ok := err.(*gdpr.ErrorMalformedConsent); ok { - return true, http.StatusBadRequest, "gdpr_consent was invalid. " + err.Error() - } else { - // We can't really distinguish between requests that are for a new version of the global vendor list, and - // ones which are simply malformed (version number is much too large). - // Since we try to fetch new versions as requests come in for them, PBS *should* self-correct - // rather quickly, meaning that most of these will be malformed strings. - return true, http.StatusBadRequest, "No global vendor list was available to interpret this consent string. If this is a new, valid version, it should become available soon." - } - } else if !allowed { - return true, http.StatusOK, "The gdpr_consent string prevents cookies from being saved" - } else { - return false, 0, "" - } - default: +func preventSyncsGDPR(gdprEnabled string, gdprConsent string, perms gdpr.Permissions) (shouldReturn bool, status int, body string) { + + if gdprEnabled != "" && gdprEnabled != "0" && gdprEnabled != "1" { return true, http.StatusBadRequest, "the gdpr query param must be either 0 or 1. You gave " + gdprEnabled } + + if gdprEnabled == "1" && gdprConsent == "" { + return true, http.StatusBadRequest, "gdpr_consent is required when gdpr=1" + } + + gdprSignal := gdpr.SignalAmbiguous + + if i, err := strconv.Atoi(gdprEnabled); err == nil { + gdprSignal = gdpr.Signal(i) + } + + allowed, err := perms.HostCookiesAllowed(context.Background(), gdprSignal, gdprConsent) + if err != nil { + if _, ok := err.(*gdpr.ErrorMalformedConsent); ok { + return true, http.StatusBadRequest, "gdpr_consent was invalid. " + err.Error() + } + + // We can't really distinguish between requests that are for a new version of the global vendor list, and + // ones which are simply malformed (version number is much too large). + // Since we try to fetch new versions as requests come in for them, PBS *should* self-correct + // rather quickly, meaning that most of these will be malformed strings. + return true, http.StatusBadRequest, "No global vendor list was available to interpret this consent string. If this is a new, valid version, it should become available soon." + } + + if allowed { + return false, 0, "" + } + + return true, http.StatusOK, "The gdpr_consent string prevents cookies from being saved" } diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 602859582c0..fc98608ef9f 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -101,12 +101,13 @@ func TestSetUIDEndpoint(t *testing.T) { description: "Add the uid for the requested bidder to the list of existing syncs", }, { - uri: "/setuid?bidder=pubmatic&uid=123&gdpr=0", - validFamilyNames: []string{"pubmatic"}, - existingSyncs: nil, - expectedSyncs: map[string]string{"pubmatic": "123"}, - expectedResponseCode: http.StatusOK, - description: "Don't care about GDPR consent if GDPR is set to 0", + uri: "/setuid?bidder=pubmatic&uid=123&gdpr=0", + validFamilyNames: []string{"pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedResponseCode: http.StatusOK, + description: "Don't care about GDPR consent if GDPR is set to 0", }, { uri: "/setuid?bidder=pubmatic&uid=123", @@ -426,7 +427,7 @@ type mockPermsSetUID struct { allowPI bool } -func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, gdprSignal gdpr.Signal, consent string) (bool, error) { var err error if g.errorHost { err = errors.New("something went wrong") @@ -434,7 +435,7 @@ func (g *mockPermsSetUID) HostCookiesAllowed(ctx context.Context, consent string return g.allowHost, err } -func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (g *mockPermsSetUID) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal gdpr.Signal, consent string) (bool, error) { return false, nil } diff --git a/exchange/utils_test.go b/exchange/utils_test.go index e679397b1dd..0407b3c5e0e 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -24,11 +24,11 @@ type permissionsMock struct { personalInfoAllowedError error } -func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (p *permissionsMock) HostCookiesAllowed(ctx context.Context, gdpr gdpr.Signal, consent string) (bool, error) { return true, nil } -func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdpr gdpr.Signal, consent string) (bool, error) { return true, nil } diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index bfc238e12a3..f24fd6c56f5 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -3,9 +3,11 @@ package gdpr import ( "context" "net/http" + "strconv" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" ) @@ -13,12 +15,12 @@ type Permissions interface { // Determines whether or not the host company is allowed to read/write cookies. // // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. - HostCookiesAllowed(ctx context.Context, consent string) (bool, error) + HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) // Determines whether or not the given bidder is allowed to user personal info for ad targeting. // // If the consent string was nonsensical, the returned error will be an ErrorMalformedConsent. - BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) + BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) // Determines whether or not to send PI information to a bidder, or mask it out. // @@ -65,3 +67,21 @@ type ErrorMalformedConsent struct { func (e *ErrorMalformedConsent) Error() string { return "malformed consent string " + e.consent + ": " + e.cause.Error() } + +// SignalParse parses a raw signal and returns +func SignalParse(rawSignal string) (Signal, error) { + if rawSignal == "" { + return SignalAmbiguous, nil + } + + i, err := strconv.Atoi(rawSignal) + + if err != nil { + return SignalAmbiguous, err + } + if i != 0 && i != 1 { + return SignalAmbiguous, &errortypes.BadInput{Message: "GDPR signal should be integer 0 or 1"} + } + + return Signal(i), nil +} diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go index 902bf14e662..5048cf118f5 100644 --- a/gdpr/gdpr_test.go +++ b/gdpr/gdpr_test.go @@ -48,3 +48,55 @@ func TestNewPermissions(t *testing.T) { assert.IsType(t, tt.wantType, perms, tt.description) } } + +func TestSignalParse(t *testing.T) { + tests := []struct { + description string + rawSignal string + wantSignal Signal + wantError bool + }{ + { + description: "valid raw signal is 0", + rawSignal: "0", + wantSignal: SignalNo, + wantError: false, + }, + { + description: "Valid signal - raw signal is 1", + rawSignal: "1", + wantSignal: SignalYes, + wantError: false, + }, + { + description: "Valid signal - raw signal is empty", + rawSignal: "", + wantSignal: SignalAmbiguous, + wantError: false, + }, + { + description: "Invalid signal - raw signal is -1", + rawSignal: "-1", + wantSignal: SignalAmbiguous, + wantError: true, + }, + { + description: "Invalid signal - raw signal is abc", + rawSignal: "abc", + wantSignal: SignalAmbiguous, + wantError: true, + }, + } + + for _, tt := range tests { + signal, err := SignalParse(tt.rawSignal) + + assert.Equal(t, tt.wantSignal, signal, tt.description) + + if tt.wantError { + assert.NotNil(t, err, tt.description) + } else { + assert.Nil(t, err, tt.description) + } + } +} diff --git a/gdpr/impl.go b/gdpr/impl.go index c7998251783..06b625da95c 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -33,20 +33,28 @@ type permissionsImpl struct { fetchVendorList map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error) } -func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (p *permissionsImpl) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) { + gdprSignal = p.normalizeGDPR(gdprSignal) + + if gdprSignal == SignalNo { + return true, nil + } + return p.allowSync(ctx, uint16(p.cfg.HostVendorID), consent) } -func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) { + gdprSignal = p.normalizeGDPR(gdprSignal) + + if gdprSignal == SignalNo { + return true, nil + } + id, ok := p.vendorIDs[bidder] if ok { return p.allowSync(ctx, id, consent) } - if consent == "" { - return p.cfg.UsersyncIfAmbiguous, nil - } - return false, nil } @@ -55,13 +63,7 @@ func (p *permissionsImpl) PersonalInfoAllowed(ctx context.Context, bidder openrt return true, true, true, nil } - if gdprSignal == SignalAmbiguous { - if p.cfg.UsersyncIfAmbiguous { - gdprSignal = SignalNo - } else { - gdprSignal = SignalYes - } - } + gdprSignal = p.normalizeGDPR(gdprSignal) if gdprSignal == SignalNo { return true, true, true, nil @@ -82,10 +84,22 @@ func (p *permissionsImpl) defaultVendorPermissions() (allowPI bool, allowGeo boo return false, false, false, nil } +func (p *permissionsImpl) normalizeGDPR(gdprSignal Signal) Signal { + if gdprSignal != SignalAmbiguous { + return gdprSignal + } + + if p.cfg.UsersyncIfAmbiguous { + return SignalNo + } + + return SignalYes +} + func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, consent string) (bool, error) { - // If we're not given a consent string, respect the preferences in the app config. + if consent == "" { - return p.cfg.UsersyncIfAmbiguous, nil + return false, nil } parsedConsent, vendor, err := p.parseVendor(ctx, vendorID, consent) @@ -229,18 +243,18 @@ type AllowHostCookies struct { } // HostCookiesAllowed always returns true -func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (p *AllowHostCookies) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) { return true, nil } // Exporting to allow for easy test setups type AlwaysAllow struct{} -func (a AlwaysAllow) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (a AlwaysAllow) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) { return true, nil } -func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (a AlwaysAllow) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) { return true, nil } @@ -251,11 +265,11 @@ func (a AlwaysAllow) PersonalInfoAllowed(ctx context.Context, bidder openrtb_ext // Exporting to allow for easy test setups type AlwaysFail struct{} -func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, consent string) (bool, error) { +func (a AlwaysFail) HostCookiesAllowed(ctx context.Context, gdprSignal Signal, consent string) (bool, error) { return false, nil } -func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, consent string) (bool, error) { +func (a AlwaysFail) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName, gdprSignal Signal, consent string) (bool, error) { return false, nil } diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index a7d4b26af67..45d2ba43ce3 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNoConsentButAllowByDefault(t *testing.T) { +func TestDisallowOnEmptyConsent(t *testing.T) { perms := permissionsImpl{ cfg: config.GDPR{ HostVendorID: 3, @@ -27,34 +27,27 @@ func TestNoConsentButAllowByDefault(t *testing.T) { tcf2SpecVersion: failedListFetcher, }, } - allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") - assertBoolsEqual(t, true, allowSync) - assertNilErr(t, err) - allowSync, err = perms.HostCookiesAllowed(context.Background(), "") - assertBoolsEqual(t, true, allowSync) - assertNilErr(t, err) -} - -func TestNoConsentAndRejectByDefault(t *testing.T) { - perms := permissionsImpl{ - cfg: config.GDPR{ - HostVendorID: 3, - UsersyncIfAmbiguous: false, - }, - vendorIDs: nil, - fetchVendorList: map[uint8]func(ctx context.Context, id uint16) (vendorlist.VendorList, error){ - tcf1SpecVersion: failedListFetcher, - tcf2SpecVersion: failedListFetcher, - }, - } - allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, "") + allowSync, err := perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, SignalYes, "") assertBoolsEqual(t, false, allowSync) assertNilErr(t, err) - allowSync, err = perms.HostCookiesAllowed(context.Background(), "") + allowSync, err = perms.HostCookiesAllowed(context.Background(), SignalYes, "") assertBoolsEqual(t, false, allowSync) assertNilErr(t, err) } +func TestAllowOnSignalNo(t *testing.T) { + perms := permissionsImpl{} + emptyConsent := "" + + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalNo, emptyConsent) + assert.Equal(t, true, allowSync) + assert.Nil(t, err) + + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderAppnexus, SignalNo, emptyConsent) + assert.Equal(t, true, allowSync) + assert.Nil(t, err) +} + func TestAllowedSyncs(t *testing.T) { vendorListData := tcf1MarshalVendorList(tcf1VendorList{ VendorListVersion: 1, @@ -81,11 +74,11 @@ func TestAllowedSyncs(t *testing.T) { }, } - allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABoAAAAAMw") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON3PCUON3PCUABABBAAABoAAAAAMw") assertNilErr(t, err) assertBoolsEqual(t, true, allowSync) - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BON3PCUON3PCUABABBAAABoAAAAAMw") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BON3PCUON3PCUABABBAAABoAAAAAMw") assertNilErr(t, err) assertBoolsEqual(t, true, allowSync) } @@ -116,11 +109,11 @@ func TestProhibitedPurposes(t *testing.T) { }, } - allowSync, err := perms.HostCookiesAllowed(context.Background(), "BON3PCUON3PCUABABBAAABAAAAAAMw") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON3PCUON3PCUABABBAAABAAAAAAMw") assertNilErr(t, err) assertBoolsEqual(t, false, allowSync) - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BON3PCUON3PCUABABBAAABAAAAAAMw") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BON3PCUON3PCUABABBAAABAAAAAAMw") assertNilErr(t, err) assertBoolsEqual(t, false, allowSync) } @@ -151,11 +144,11 @@ func TestProhibitedVendors(t *testing.T) { }, } - allowSync, err := perms.HostCookiesAllowed(context.Background(), "BOS2bx5OS2bx5ABABBAAABoAAAAAFA") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA") assertNilErr(t, err) assertBoolsEqual(t, false, allowSync) - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderPubmatic, SignalYes, "BOS2bx5OS2bx5ABABBAAABoAAAAAFA") assertNilErr(t, err) assertBoolsEqual(t, false, allowSync) } @@ -171,7 +164,7 @@ func TestMalformedConsent(t *testing.T) { }, } - sync, err := perms.HostCookiesAllowed(context.Background(), "BON") + sync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "BON") assertErr(t, err, true) assertBoolsEqual(t, false, sync) } @@ -618,11 +611,11 @@ func TestAllowSyncTCF2(t *testing.T) { } // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consensts to purposes and vendors 2, 6, 8 - allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") assert.EqualValuesf(t, true, allowSync, "HostCookiesAllowed failure") - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") assert.EqualValuesf(t, true, allowSync, "BidderSyncAllowed failure") } @@ -648,11 +641,11 @@ func TestProhibitedPurposeSyncTCF2(t *testing.T) { perms.cfg.HostVendorID = 8 // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8 - allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderRubicon, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure") } @@ -677,12 +670,12 @@ func TestProhibitedVendorSyncTCF2(t *testing.T) { perms.cfg.HostVendorID = 10 // COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA : TCF2 with full consents to purposes for vendors 2, 6, 8 - allowSync, err := perms.HostCookiesAllowed(context.Background(), "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err := perms.HostCookiesAllowed(context.Background(), SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing HostCookiesAllowed") assert.EqualValuesf(t, false, allowSync, "HostCookiesAllowed failure") // Permission disallowed due to consent string not including vendor 10. - allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderOpenx, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") + allowSync, err = perms.BidderSyncAllowed(context.Background(), openrtb_ext.BidderOpenx, SignalYes, "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA") assert.NoErrorf(t, err, "Error processing BidderSyncAllowed") assert.EqualValuesf(t, false, allowSync, "BidderSyncAllowed failure") } @@ -750,3 +743,61 @@ func assertStringsEqual(t *testing.T, expected string, actual string) { t.Errorf("Expected %s, got %s", expected, actual) } } + +func TestNormalizeGDPR(t *testing.T) { + tests := []struct { + description string + userSyncIfAmbiguous bool + giveSignal Signal + wantSignal Signal + }{ + { + description: "Don't normalize - Signal No and userSyncIfAmbiguous false", + userSyncIfAmbiguous: false, + giveSignal: SignalNo, + wantSignal: SignalNo, + }, + { + description: "Don't normalize - Signal No and userSyncIfAmbiguous true", + userSyncIfAmbiguous: true, + giveSignal: SignalNo, + wantSignal: SignalNo, + }, + { + description: "Don't normalize - Signal Yes and userSyncIfAmbiguous false", + userSyncIfAmbiguous: false, + giveSignal: SignalYes, + wantSignal: SignalYes, + }, + { + description: "Don't normalize - Signal Yes and userSyncIfAmbiguous true", + userSyncIfAmbiguous: true, + giveSignal: SignalYes, + wantSignal: SignalYes, + }, + { + description: "Normalize - Signal Ambiguous and userSyncIfAmbiguous false", + userSyncIfAmbiguous: false, + giveSignal: SignalAmbiguous, + wantSignal: SignalYes, + }, + { + description: "Normalize - Signal Ambiguous and userSyncIfAmbiguous true", + userSyncIfAmbiguous: true, + giveSignal: SignalAmbiguous, + wantSignal: SignalNo, + }, + } + + for _, tt := range tests { + perms := permissionsImpl{ + cfg: config.GDPR{ + UsersyncIfAmbiguous: tt.userSyncIfAmbiguous, + }, + } + + normalizedSignal := perms.normalizeGDPR(tt.giveSignal) + + assert.Equal(t, tt.wantSignal, normalizedSignal, tt.description) + } +} From edda1f69a9ae99e75eb76e9c9412facb7b3c50dc Mon Sep 17 00:00:00 2001 From: Isha Bharti Date: Thu, 11 Feb 2021 13:04:47 +0530 Subject: [PATCH 316/318] Prebid Upgrade 0.146.0: Updating import statements --- .travis.yml | 2 +- Makefile | 2 +- README.md | 10 +- account/account.go | 8 +- account/account_test.go | 8 +- adapters/33across/33across.go | 10 +- adapters/33across/33across_test.go | 6 +- adapters/33across/params_test.go | 2 +- adapters/33across/usersync.go | 4 +- adapters/33across/usersync_test.go | 6 +- adapters/acuityads/acuityads.go | 12 +- adapters/acuityads/acuityads_test.go | 6 +- adapters/acuityads/params_test.go | 2 +- adapters/acuityads/usersync.go | 4 +- adapters/acuityads/usersync_test.go | 6 +- adapters/adapterstest/adapter_test_util.go | 2 +- adapters/adapterstest/test_json.go | 4 +- adapters/adform/adform.go | 12 +- adapters/adform/adform_test.go | 16 +- adapters/adform/params_test.go | 2 +- adapters/adform/usersync.go | 4 +- adapters/adform/usersync_test.go | 4 +- adapters/adgeneration/adgeneration.go | 10 +- adapters/adgeneration/adgeneration_test.go | 10 +- adapters/adgeneration/params_test.go | 2 +- adapters/adhese/adhese.go | 12 +- adapters/adhese/adhese_test.go | 6 +- adapters/adhese/params_test.go | 2 +- adapters/adhese/utils.go | 2 +- adapters/adkernel/adkernel.go | 12 +- adapters/adkernel/adkernel_test.go | 6 +- adapters/adkernel/usersync.go | 4 +- adapters/adkernel/usersync_test.go | 6 +- adapters/adkernelAdn/adkernelAdn.go | 12 +- adapters/adkernelAdn/adkernelAdn_test.go | 6 +- adapters/adkernelAdn/usersync.go | 4 +- adapters/adkernelAdn/usersync_test.go | 6 +- adapters/adman/adman.go | 10 +- adapters/adman/adman_test.go | 6 +- adapters/adman/params_test.go | 2 +- adapters/adman/usersync.go | 4 +- adapters/adman/usersync_test.go | 6 +- adapters/admixer/admixer.go | 10 +- adapters/admixer/admixer_test.go | 6 +- adapters/admixer/params_test.go | 2 +- adapters/admixer/usersync.go | 4 +- adapters/admixer/usersync_test.go | 6 +- adapters/adocean/adocean.go | 12 +- adapters/adocean/adocean_test.go | 6 +- adapters/adocean/params_test.go | 2 +- adapters/adocean/usersync.go | 4 +- adapters/adocean/usersync_test.go | 4 +- adapters/adoppler/adoppler.go | 12 +- adapters/adoppler/adoppler_test.go | 6 +- adapters/adot/adot.go | 10 +- adapters/adot/adot_test.go | 10 +- adapters/adot/params_test.go | 2 +- adapters/adpone/adpone.go | 10 +- adapters/adpone/adpone_test.go | 6 +- adapters/adpone/usersync.go | 4 +- adapters/adpone/usersync_test.go | 2 +- adapters/adprime/adprime.go | 10 +- adapters/adprime/adprime_test.go | 6 +- adapters/adprime/params_test.go | 2 +- adapters/adtarget/adtarget.go | 10 +- adapters/adtarget/adtarget_test.go | 6 +- adapters/adtarget/params_test.go | 2 +- adapters/adtarget/usersync.go | 4 +- adapters/adtarget/usersync_test.go | 6 +- adapters/adtelligent/adtelligent.go | 10 +- adapters/adtelligent/adtelligent_test.go | 6 +- adapters/adtelligent/params_test.go | 2 +- adapters/adtelligent/usersync.go | 4 +- adapters/adtelligent/usersync_test.go | 4 +- adapters/advangelists/advangelists.go | 12 +- adapters/advangelists/advangelists_test.go | 6 +- adapters/advangelists/params_test.go | 2 +- adapters/advangelists/usersync.go | 4 +- adapters/advangelists/usersync_test.go | 4 +- adapters/aja/aja.go | 10 +- adapters/aja/aja_test.go | 6 +- adapters/aja/usersync.go | 4 +- adapters/aja/usersync_test.go | 6 +- adapters/amx/amx.go | 10 +- adapters/amx/amx_test.go | 10 +- adapters/amx/params_test.go | 2 +- adapters/amx/usersync.go | 4 +- adapters/amx/usersync_test.go | 2 +- adapters/applogy/applogy.go | 10 +- adapters/applogy/applogy_test.go | 6 +- adapters/appnexus/appnexus.go | 14 +- adapters/appnexus/appnexus_test.go | 16 +- adapters/appnexus/params_test.go | 2 +- adapters/appnexus/usersync.go | 4 +- adapters/appnexus/usersync_test.go | 2 +- adapters/audienceNetwork/facebook.go | 12 +- adapters/audienceNetwork/facebook_test.go | 8 +- adapters/audienceNetwork/usersync.go | 4 +- adapters/audienceNetwork/usersync_test.go | 2 +- adapters/avocet/avocet.go | 10 +- adapters/avocet/avocet_test.go | 12 +- adapters/avocet/usersync.go | 4 +- adapters/avocet/usersync_test.go | 6 +- adapters/beachfront/beachfront.go | 10 +- adapters/beachfront/beachfront_test.go | 6 +- adapters/beachfront/params_test.go | 2 +- adapters/beachfront/usersync.go | 4 +- adapters/beachfront/usersync_test.go | 6 +- adapters/beintoo/beintoo.go | 10 +- adapters/beintoo/beintoo_test.go | 6 +- adapters/beintoo/params_test.go | 2 +- adapters/beintoo/usersync.go | 4 +- adapters/beintoo/usersync_test.go | 6 +- adapters/between/between.go | 12 +- adapters/between/between_test.go | 6 +- adapters/between/params_test.go | 2 +- adapters/between/usersync.go | 4 +- adapters/between/usersync_test.go | 2 +- adapters/bidder.go | 8 +- adapters/brightroll/brightroll.go | 10 +- adapters/brightroll/brightroll_test.go | 6 +- adapters/brightroll/params_test.go | 2 +- adapters/brightroll/usersync.go | 4 +- adapters/brightroll/usersync_test.go | 2 +- adapters/colossus/colossus.go | 10 +- adapters/colossus/colossus_test.go | 6 +- adapters/colossus/params_test.go | 2 +- adapters/colossus/usersync.go | 4 +- adapters/colossus/usersync_test.go | 6 +- adapters/connectad/connectad.go | 10 +- adapters/connectad/connectad_test.go | 6 +- adapters/connectad/params_test.go | 2 +- adapters/connectad/usersync.go | 4 +- adapters/connectad/usersync_test.go | 6 +- adapters/consumable/adtypes.go | 2 +- adapters/consumable/consumable.go | 12 +- adapters/consumable/consumable_test.go | 8 +- adapters/consumable/params_test.go | 2 +- adapters/consumable/usersync.go | 4 +- adapters/consumable/usersync_test.go | 6 +- adapters/conversant/cnvr_legacy.go | 8 +- adapters/conversant/cnvr_legacy_test.go | 12 +- adapters/conversant/conversant.go | 10 +- adapters/conversant/conversant_test.go | 6 +- adapters/conversant/usersync.go | 4 +- adapters/conversant/usersync_test.go | 4 +- adapters/cpmstar/cpmstar.go | 10 +- adapters/cpmstar/cpmstar_test.go | 6 +- adapters/cpmstar/params_test.go | 2 +- adapters/cpmstar/usersync.go | 4 +- adapters/cpmstar/usersync_test.go | 2 +- adapters/datablocks/datablocks.go | 12 +- adapters/datablocks/datablocks_test.go | 6 +- adapters/datablocks/usersync.go | 4 +- adapters/datablocks/usersync_test.go | 6 +- adapters/decenterads/decenterads.go | 10 +- adapters/decenterads/decenterads_test.go | 6 +- adapters/decenterads/params_test.go | 2 +- adapters/deepintent/deepintent.go | 10 +- adapters/deepintent/deepintent_test.go | 6 +- adapters/deepintent/params_test.go | 2 +- adapters/deepintent/usersync.go | 4 +- adapters/deepintent/usersync_test.go | 6 +- adapters/dmx/dmx.go | 10 +- adapters/dmx/dmx_test.go | 10 +- adapters/dmx/params_test.go | 2 +- adapters/dmx/usersync.go | 4 +- adapters/dmx/usersync_test.go | 2 +- adapters/emx_digital/emx_digital.go | 10 +- adapters/emx_digital/emx_digital_test.go | 8 +- adapters/emx_digital/params_test.go | 2 +- adapters/emx_digital/usersync.go | 4 +- adapters/emx_digital/usersync_test.go | 6 +- adapters/engagebdr/engagebdr.go | 10 +- adapters/engagebdr/engagebdr_test.go | 6 +- adapters/engagebdr/params_test.go | 2 +- adapters/engagebdr/usersync.go | 4 +- adapters/engagebdr/usersync_test.go | 6 +- adapters/eplanning/eplanning.go | 10 +- adapters/eplanning/eplanning_test.go | 8 +- adapters/eplanning/usersync.go | 4 +- adapters/eplanning/usersync_test.go | 2 +- adapters/gamma/gamma.go | 10 +- adapters/gamma/gamma_test.go | 6 +- adapters/gamma/params_test.go | 2 +- adapters/gamma/usersync.go | 4 +- adapters/gamma/usersync_test.go | 4 +- adapters/gamoshi/gamoshi.go | 10 +- adapters/gamoshi/gamoshi_test.go | 6 +- adapters/gamoshi/params_test.go | 2 +- adapters/gamoshi/usersync.go | 4 +- adapters/gamoshi/usersync_test.go | 4 +- adapters/grid/grid.go | 10 +- adapters/grid/grid_test.go | 6 +- adapters/grid/usersync.go | 4 +- adapters/grid/usersync_test.go | 4 +- adapters/gumgum/gumgum.go | 10 +- adapters/gumgum/gumgum_test.go | 6 +- adapters/gumgum/params_test.go | 2 +- adapters/gumgum/usersync.go | 4 +- adapters/gumgum/usersync_test.go | 6 +- adapters/improvedigital/improvedigital.go | 10 +- .../improvedigital/improvedigital_test.go | 6 +- adapters/improvedigital/params_test.go | 2 +- adapters/improvedigital/usersync.go | 4 +- adapters/improvedigital/usersync_test.go | 6 +- adapters/info.go | 8 +- adapters/info_test.go | 10 +- adapters/inmobi/inmobi.go | 10 +- adapters/inmobi/inmobi_test.go | 6 +- adapters/invibes/invibes.go | 14 +- adapters/invibes/invibes_test.go | 6 +- adapters/invibes/params_test.go | 2 +- adapters/invibes/usersync.go | 4 +- adapters/invibes/usersync_test.go | 4 +- adapters/ix/ix.go | 12 +- adapters/ix/ix_test.go | 12 +- adapters/ix/usersync.go | 4 +- adapters/ix/usersync_test.go | 2 +- adapters/kidoz/kidoz.go | 10 +- adapters/kidoz/kidoz_test.go | 10 +- adapters/kidoz/params_test.go | 2 +- adapters/krushmedia/krushmedia.go | 12 +- adapters/krushmedia/krushmedia_test.go | 6 +- adapters/krushmedia/params_test.go | 2 +- adapters/krushmedia/usersync.go | 4 +- adapters/krushmedia/usersync_test.go | 6 +- adapters/kubient/kubient.go | 10 +- adapters/kubient/kubient_test.go | 6 +- adapters/legacy.go | 4 +- adapters/lifestreet/lifestreet.go | 10 +- adapters/lifestreet/lifestreet_test.go | 12 +- adapters/lifestreet/usersync.go | 4 +- adapters/lifestreet/usersync_test.go | 4 +- adapters/lockerdome/lockerdome.go | 10 +- adapters/lockerdome/lockerdome_test.go | 6 +- adapters/lockerdome/params_test.go | 2 +- adapters/lockerdome/usersync.go | 4 +- adapters/lockerdome/usersync_test.go | 2 +- adapters/logicad/logicad.go | 10 +- adapters/logicad/logicad_test.go | 6 +- adapters/logicad/params_test.go | 2 +- adapters/logicad/usersync.go | 4 +- adapters/logicad/usersync_test.go | 4 +- adapters/lunamedia/lunamedia.go | 12 +- adapters/lunamedia/lunamedia_test.go | 6 +- adapters/lunamedia/params_test.go | 2 +- adapters/lunamedia/usersync.go | 4 +- adapters/lunamedia/usersync_test.go | 4 +- adapters/marsmedia/marsmedia.go | 10 +- adapters/marsmedia/marsmedia_test.go | 6 +- adapters/marsmedia/params_test.go | 2 +- adapters/marsmedia/usersync.go | 4 +- adapters/marsmedia/usersync_test.go | 6 +- adapters/mediafuse/usersync.go | 4 +- adapters/mediafuse/usersync_test.go | 4 +- adapters/mgid/mgid.go | 10 +- adapters/mgid/mgid_test.go | 6 +- adapters/mgid/usersync.go | 4 +- adapters/mgid/usersync_test.go | 4 +- adapters/mobfoxpb/mobfoxpb.go | 10 +- adapters/mobfoxpb/mobfoxpb_test.go | 6 +- adapters/mobfoxpb/params_test.go | 2 +- adapters/mobilefuse/mobilefuse.go | 12 +- adapters/mobilefuse/mobilefuse_test.go | 6 +- adapters/mobilefuse/params_test.go | 2 +- adapters/nanointeractive/nanointeractive.go | 10 +- .../nanointeractive/nanointeractive_test.go | 6 +- adapters/nanointeractive/params_test.go | 2 +- adapters/nanointeractive/usersync.go | 4 +- adapters/nanointeractive/usersync_test.go | 6 +- adapters/ninthdecimal/ninthdecimal.go | 12 +- adapters/ninthdecimal/ninthdecimal_test.go | 6 +- adapters/ninthdecimal/params_test.go | 2 +- adapters/ninthdecimal/usersync.go | 4 +- adapters/ninthdecimal/usersync_test.go | 4 +- adapters/nobid/nobid.go | 10 +- adapters/nobid/nobid_test.go | 6 +- adapters/nobid/params_test.go | 2 +- adapters/nobid/usersync.go | 4 +- adapters/nobid/usersync_test.go | 6 +- adapters/openrtb_util.go | 6 +- adapters/openrtb_util_test.go | 6 +- adapters/openx/openx.go | 10 +- adapters/openx/openx_test.go | 10 +- adapters/openx/params_test.go | 2 +- adapters/openx/usersync.go | 4 +- adapters/openx/usersync_test.go | 2 +- adapters/orbidder/orbidder.go | 10 +- adapters/orbidder/orbidder_test.go | 6 +- adapters/orbidder/params_test.go | 2 +- adapters/pubmatic/params_test.go | 2 +- adapters/pubmatic/pubmatic.go | 12 +- adapters/pubmatic/pubmatic_test.go | 16 +- adapters/pubmatic/usersync.go | 4 +- adapters/pubmatic/usersync_test.go | 6 +- adapters/pubnative/pubnative.go | 10 +- adapters/pubnative/pubnative_test.go | 6 +- adapters/pulsepoint/params_test.go | 2 +- adapters/pulsepoint/pulsepoint.go | 12 +- adapters/pulsepoint/pulsepoint_test.go | 16 +- adapters/pulsepoint/usersync.go | 4 +- adapters/pulsepoint/usersync_test.go | 2 +- adapters/revcontent/revcontent.go | 10 +- adapters/revcontent/revcontent_test.go | 6 +- adapters/rhythmone/params_test.go | 2 +- adapters/rhythmone/rhythmone.go | 10 +- adapters/rhythmone/rhythmone_test.go | 6 +- adapters/rhythmone/usersync.go | 4 +- adapters/rhythmone/usersync_test.go | 6 +- adapters/rtbhouse/rtbhouse.go | 10 +- adapters/rtbhouse/rtbhouse_test.go | 6 +- adapters/rtbhouse/usersync.go | 4 +- adapters/rtbhouse/usersync_test.go | 4 +- adapters/rubicon/rubicon.go | 12 +- adapters/rubicon/rubicon_test.go | 18 +- adapters/rubicon/usersync.go | 4 +- adapters/rubicon/usersync_test.go | 4 +- adapters/sharethrough/butler.go | 10 +- adapters/sharethrough/butler_test.go | 8 +- adapters/sharethrough/params_test.go | 2 +- adapters/sharethrough/sharethrough.go | 10 +- adapters/sharethrough/sharethrough_test.go | 10 +- adapters/sharethrough/usersync.go | 4 +- adapters/sharethrough/usersync_test.go | 4 +- adapters/sharethrough/utils.go | 4 +- adapters/sharethrough/utils_test.go | 4 +- adapters/silvermob/params_test.go | 2 +- adapters/silvermob/silvermob.go | 12 +- adapters/silvermob/silvermob_test.go | 6 +- adapters/smaato/params_test.go | 2 +- adapters/smaato/smaato.go | 10 +- adapters/smaato/smaato_test.go | 6 +- adapters/smartadserver/params_test.go | 2 +- adapters/smartadserver/smartadserver.go | 10 +- adapters/smartadserver/smartadserver_test.go | 6 +- adapters/smartadserver/usersync.go | 4 +- adapters/smartadserver/usersync_test.go | 6 +- adapters/smartrtb/smartrtb.go | 12 +- adapters/smartrtb/smartrtb_test.go | 6 +- adapters/smartrtb/usersync.go | 4 +- adapters/smartrtb/usersync_test.go | 2 +- adapters/smartyads/params_test.go | 2 +- adapters/smartyads/smartyads.go | 12 +- adapters/smartyads/smartyads_test.go | 6 +- adapters/smartyads/usersync.go | 4 +- adapters/smartyads/usersync_test.go | 6 +- adapters/somoaudience/params_test.go | 2 +- adapters/somoaudience/somoaudience.go | 10 +- adapters/somoaudience/somoaudience_test.go | 6 +- adapters/somoaudience/usersync.go | 4 +- adapters/somoaudience/usersync_test.go | 2 +- adapters/sonobi/params_test.go | 2 +- adapters/sonobi/sonobi.go | 10 +- adapters/sonobi/sonobi_test.go | 6 +- adapters/sonobi/usersync.go | 4 +- adapters/sonobi/usersync_test.go | 4 +- adapters/sovrn/sovrn.go | 12 +- adapters/sovrn/sovrn_test.go | 16 +- adapters/sovrn/usersync.go | 4 +- adapters/sovrn/usersync_test.go | 4 +- adapters/synacormedia/params_test.go | 2 +- adapters/synacormedia/synacormedia.go | 12 +- adapters/synacormedia/synacormedia_test.go | 6 +- adapters/synacormedia/usersync.go | 4 +- adapters/synacormedia/usersync_test.go | 2 +- adapters/syncer.go | 8 +- adapters/syncer_test.go | 6 +- adapters/tappx/params_test.go | 2 +- adapters/tappx/tappx.go | 12 +- adapters/tappx/tappx_test.go | 6 +- adapters/telaria/params_test.go | 2 +- adapters/telaria/telaria.go | 10 +- adapters/telaria/telaria_test.go | 6 +- adapters/telaria/usersync.go | 4 +- adapters/telaria/usersync_test.go | 4 +- adapters/triplelift/triplelift.go | 10 +- adapters/triplelift/triplelift_test.go | 6 +- adapters/triplelift/usersync.go | 4 +- adapters/triplelift/usersync_test.go | 2 +- .../triplelift_native/triplelift_native.go | 10 +- .../triplelift_native_test.go | 6 +- adapters/triplelift_native/usersync.go | 4 +- adapters/triplelift_native/usersync_test.go | 2 +- adapters/ucfunnel/params_test.go | 2 +- adapters/ucfunnel/ucfunnel.go | 10 +- adapters/ucfunnel/ucfunnel_test.go | 8 +- adapters/ucfunnel/usersync.go | 4 +- adapters/ucfunnel/usersync_test.go | 4 +- adapters/unruly/params_test.go | 2 +- adapters/unruly/unruly.go | 10 +- adapters/unruly/unruly_test.go | 12 +- adapters/unruly/usersync.go | 4 +- adapters/unruly/usersync_test.go | 6 +- adapters/valueimpression/params_test.go | 2 +- adapters/valueimpression/usersync.go | 4 +- adapters/valueimpression/usersync_test.go | 6 +- adapters/valueimpression/valueimpression.go | 10 +- .../valueimpression/valueimpression_test.go | 6 +- adapters/verizonmedia/params_test.go | 2 +- adapters/verizonmedia/usersync.go | 4 +- adapters/verizonmedia/usersync_test.go | 2 +- adapters/verizonmedia/verizonmedia.go | 10 +- adapters/verizonmedia/verizonmedia_test.go | 6 +- adapters/visx/params_test.go | 2 +- adapters/visx/usersync.go | 4 +- adapters/visx/usersync_test.go | 6 +- adapters/visx/visx.go | 10 +- adapters/visx/visx_test.go | 6 +- adapters/vrtcal/params_test.go | 2 +- adapters/vrtcal/usersync.go | 4 +- adapters/vrtcal/usersync_test.go | 4 +- adapters/vrtcal/vrtcal.go | 10 +- adapters/vrtcal/vrtcal_test.go | 6 +- adapters/yeahmobi/params_test.go | 2 +- adapters/yeahmobi/yeahmobi.go | 12 +- adapters/yeahmobi/yeahmobi_test.go | 6 +- adapters/yieldlab/params_test.go | 2 +- adapters/yieldlab/usersync.go | 4 +- adapters/yieldlab/usersync_test.go | 4 +- adapters/yieldlab/yieldlab.go | 10 +- adapters/yieldlab/yieldlab_test.go | 6 +- adapters/yieldmo/params_test.go | 2 +- adapters/yieldmo/usersync.go | 4 +- adapters/yieldmo/usersync_test.go | 4 +- adapters/yieldmo/yieldmo.go | 10 +- adapters/yieldmo/yieldmo_test.go | 6 +- adapters/yieldone/params_test.go | 2 +- adapters/yieldone/usersync.go | 4 +- adapters/yieldone/usersync_test.go | 4 +- adapters/yieldone/yieldone.go | 10 +- adapters/yieldone/yieldone_test.go | 6 +- adapters/zeroclickfraud/usersync.go | 4 +- adapters/zeroclickfraud/usersync_test.go | 6 +- adapters/zeroclickfraud/zeroclickfraud.go | 12 +- .../zeroclickfraud/zeroclickfraud_test.go | 6 +- amp/parse.go | 2 +- amp/parse_test.go | 2 +- analytics/clients/http.go | 2 +- analytics/config/config.go | 10 +- analytics/config/config_test.go | 6 +- analytics/core.go | 8 +- analytics/filesystem/file_module.go | 2 +- analytics/filesystem/file_module_test.go | 8 +- analytics/pubstack/helpers/json.go | 2 +- analytics/pubstack/helpers/json_test.go | 6 +- analytics/pubstack/pubstack_module.go | 6 +- analytics/pubstack/pubstack_module_test.go | 4 +- cache/dummycache/dummycache.go | 2 +- cache/filecache/filecache.go | 2 +- cache/postgrescache/postgrescache.go | 4 +- config/adapter.go | 2 +- config/config.go | 6 +- config/config_test.go | 2 +- currency/rate_converter.go | 4 +- currency/rate_converter_test.go | 2 +- diff | 10811 ++++++++++++++++ docs/developers/automated-tests.md | 2 +- docs/developers/code-reviews.md | 4 +- docs/developers/contributing.md | 6 +- endpoints/auction.go | 26 +- endpoints/auction_test.go | 22 +- endpoints/cookie_sync.go | 18 +- endpoints/cookie_sync_test.go | 20 +- endpoints/currency_rates.go | 2 +- endpoints/currency_rates_test.go | 2 +- endpoints/events/account_test.go | 6 +- endpoints/events/event.go | 10 +- endpoints/events/event_test.go | 6 +- endpoints/events/vtrack.go | 14 +- endpoints/events/vtrack_test.go | 8 +- endpoints/getuids.go | 4 +- endpoints/getuids_test.go | 2 +- endpoints/info/bidders.go | 4 +- endpoints/info/bidders_test.go | 8 +- endpoints/openrtb2/amp_auction.go | 32 +- endpoints/openrtb2/amp_auction_test.go | 18 +- endpoints/openrtb2/auction.go | 36 +- endpoints/openrtb2/auction_benchmark_test.go | 16 +- endpoints/openrtb2/auction_test.go | 24 +- endpoints/openrtb2/interstitial.go | 8 +- endpoints/openrtb2/interstitial_test.go | 2 +- endpoints/openrtb2/video_auction.go | 24 +- endpoints/openrtb2/video_auction_test.go | 18 +- endpoints/setuid.go | 12 +- endpoints/setuid_test.go | 16 +- exchange/adapter_builders.go | 192 +- exchange/adapter_util.go | 10 +- exchange/adapter_util_test.go | 18 +- exchange/auction.go | 8 +- exchange/auction_test.go | 8 +- exchange/bidder.go | 22 +- exchange/bidder_test.go | 18 +- exchange/bidder_validate_bids.go | 8 +- exchange/bidder_validate_bids_test.go | 8 +- exchange/events.go | 12 +- exchange/events_test.go | 4 +- exchange/exchange.go | 22 +- exchange/exchange_test.go | 34 +- exchange/gdpr.go | 4 +- exchange/gdpr_test.go | 4 +- exchange/legacy.go | 12 +- exchange/legacy_test.go | 12 +- exchange/price_granularity.go | 2 +- exchange/price_granularity_test.go | 2 +- exchange/targeting.go | 4 +- exchange/targeting_test.go | 16 +- exchange/utils.go | 16 +- exchange/utils_test.go | 12 +- gdpr/gdpr.go | 6 +- gdpr/gdpr_test.go | 4 +- gdpr/impl.go | 6 +- gdpr/impl_test.go | 4 +- gdpr/vendorlist-fetching.go | 4 +- gdpr/vendorlist-fetching_test.go | 2 +- go.mod | 7 +- go.sum | 16 +- main.go | 12 +- main_test.go | 2 +- metrics/config/metrics.go | 8 +- metrics/config/metrics_test.go | 6 +- metrics/go_metrics.go | 4 +- metrics/go_metrics_test.go | 4 +- metrics/metrics.go | 2 +- metrics/metrics_mock.go | 2 +- metrics/prometheus/preload.go | 2 +- metrics/prometheus/prometheus.go | 6 +- metrics/prometheus/prometheus_test.go | 6 +- metrics/prometheus/type_conversion.go | 4 +- openrtb_ext/bid_request_video.go | 2 +- openrtb_ext/deal_tier.go | 2 +- openrtb_ext/deal_tier_test.go | 2 +- openrtb_ext/device.go | 2 +- openrtb_ext/device_test.go | 2 +- openrtb_ext/response.go | 2 +- openrtb_ext/site_test.go | 2 +- pbs/pbsrequest.go | 16 +- pbs/pbsrequest_test.go | 4 +- pbs/usersync.go | 10 +- prebid_cache_client/client.go | 4 +- prebid_cache_client/client_test.go | 6 +- privacy/ccpa/consentwriter.go | 2 +- privacy/ccpa/consentwriter_test.go | 2 +- privacy/ccpa/parsedpolicy.go | 2 +- privacy/ccpa/parsedpolicy_test.go | 2 +- privacy/ccpa/policy.go | 4 +- privacy/ccpa/policy_test.go | 2 +- privacy/enforcement.go | 2 +- privacy/enforcement_test.go | 2 +- privacy/gdpr/consentwriter.go | 4 +- privacy/gdpr/consentwriter_test.go | 2 +- privacy/lmt/policy.go | 2 +- privacy/lmt/policy_test.go | 2 +- privacy/policies.go | 6 +- privacy/scrubber.go | 2 +- privacy/scrubber_test.go | 2 +- privacy/writer.go | 2 +- privacy/writer_test.go | 2 +- router/admin.go | 4 +- router/aspects/request_timeout_handler.go | 4 +- .../aspects/request_timeout_handler_test.go | 4 +- router/router.go | 70 +- router/router_test.go | 4 +- server/listener.go | 2 +- server/listener_test.go | 4 +- server/prometheus.go | 6 +- server/server.go | 6 +- server/server_test.go | 2 +- .../backends/db_fetcher/fetcher.go | 2 +- .../backends/empty_fetcher/fetcher.go | 2 +- .../backends/file_fetcher/fetcher.go | 2 +- .../backends/file_fetcher/fetcher_test.go | 2 +- .../backends/http_fetcher/fetcher.go | 2 +- stored_requests/caches/cachestest/reliable.go | 2 +- stored_requests/caches/memory/cache.go | 2 +- stored_requests/caches/memory/cache_test.go | 4 +- stored_requests/config/config.go | 30 +- stored_requests/config/config_test.go | 14 +- stored_requests/events/api/api.go | 2 +- stored_requests/events/api/api_test.go | 6 +- stored_requests/events/events.go | 2 +- stored_requests/events/events_test.go | 4 +- stored_requests/events/http/http.go | 2 +- stored_requests/events/postgres/database.go | 8 +- .../events/postgres/database_test.go | 6 +- stored_requests/fetcher.go | 2 +- stored_requests/fetcher_test.go | 4 +- usersync/cookie.go | 4 +- usersync/cookie_test.go | 4 +- usersync/usersync.go | 2 +- usersync/usersyncers/syncer.go | 160 +- usersync/usersyncers/syncer_test.go | 4 +- util/httputil/httputil.go | 2 +- util/httputil/httputil_test.go | 2 +- util/task/ticker_task_test.go | 2 +- 595 files changed, 12862 insertions(+), 2066 deletions(-) create mode 100644 diff diff --git a/.travis.yml b/.travis.yml index 97d8cea4d0d..655ea837eae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ go: - '1.14.2' - '1.15' -go_import_path: github.com/prebid/prebid-server +go_import_path: github.com/PubMatic-OpenWrap/prebid-server env: - GO111MODULE=on diff --git a/Makefile b/Makefile index 8ffea91fe36..8475ce8369b 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ test: deps ifeq "$(adapter)" "" ./validate.sh else - go test github.com/prebid/prebid-server/adapters/$(adapter) -bench=. + go test github.com/PubMatic-OpenWrap/prebid-server/adapters/$(adapter) -bench=. endif # build will ensure all of our tests pass and then build the go binary diff --git a/README.md b/README.md index 32bf4575c94..529629e0e2a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Build Status](https://travis-ci.org/prebid/prebid-server.svg?branch=master)](https://travis-ci.org/prebid/prebid-server) -[![Go Report Card](https://goreportcard.com/badge/github.com/prebid/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/prebid/prebid-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/PubMatic-OpenWrap/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/PubMatic-OpenWrap/prebid-server) # Prebid Server @@ -27,8 +27,8 @@ Download and prepare Prebid Server: ```bash cd YOUR_DIRECTORY -git clone https://github.com/prebid/prebid-server src/github.com/prebid/prebid-server -cd src/github.com/prebid/prebid-server +git clone https://github.com/PubMatic-OpenWrap/prebid-server src/github.com/PubMatic-OpenWrap/prebid-server +cd src/github.com/PubMatic-OpenWrap/prebid-server ``` Run the automated tests: @@ -52,9 +52,9 @@ For the full API reference, see [the endpoint documentation](https://docs.prebid Want to [add an adapter](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html)? Found a bug? Great! -Report bugs, request features, and suggest improvements [on Github](https://github.com/prebid/prebid-server/issues). +Report bugs, request features, and suggest improvements [on Github](https://github.com/PubMatic-OpenWrap/prebid-server/issues). -Or better yet, [open a pull request](https://github.com/prebid/prebid-server/compare) with the changes you'd like to see. +Or better yet, [open a pull request](https://github.com/PubMatic-OpenWrap/prebid-server/compare) with the changes you'd like to see. ## IDE Setup for PBS-Go development diff --git a/account/account.go b/account/account.go index 25a504ca23f..d5d22f4a894 100644 --- a/account/account.go +++ b/account/account.go @@ -5,11 +5,11 @@ import ( "encoding/json" "fmt" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" jsonpatch "github.com/evanphx/json-patch" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" ) // GetAccount looks up the config.Account object referenced by the given accountID, with access rules applied diff --git a/account/account_test.go b/account/account_test.go index 75c48a02d89..0783ee0e134 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -6,10 +6,10 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/stretchr/testify/assert" ) diff --git a/adapters/33across/33across.go b/adapters/33across/33across.go index b9655faab7f..7abd7ba72bd 100644 --- a/adapters/33across/33across.go +++ b/adapters/33across/33across.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type TtxAdapter struct { diff --git a/adapters/33across/33across_test.go b/adapters/33across/33across_test.go index 97703735d9c..08c2ff614e9 100644 --- a/adapters/33across/33across_test.go +++ b/adapters/33across/33across_test.go @@ -3,9 +3,9 @@ package ttx import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/33across/params_test.go b/adapters/33across/params_test.go index 0c7cde18216..19dfb22198c 100644 --- a/adapters/33across/params_test.go +++ b/adapters/33across/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/33across.json diff --git a/adapters/33across/usersync.go b/adapters/33across/usersync.go index 7bc9ae458ae..0bed70ee60d 100644 --- a/adapters/33across/usersync.go +++ b/adapters/33across/usersync.go @@ -3,8 +3,8 @@ package ttx import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func New33AcrossSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/33across/usersync_test.go b/adapters/33across/usersync_test.go index a9eb4e57908..89cae0f3f19 100644 --- a/adapters/33across/usersync_test.go +++ b/adapters/33across/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/acuityads/acuityads.go b/adapters/acuityads/acuityads.go index 9c6f73c27f0..ac2c6ac7c4d 100644 --- a/adapters/acuityads/acuityads.go +++ b/adapters/acuityads/acuityads.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AcuityAdsAdapter struct { diff --git a/adapters/acuityads/acuityads_test.go b/adapters/acuityads/acuityads_test.go index 8cc50637374..be12780a778 100644 --- a/adapters/acuityads/acuityads_test.go +++ b/adapters/acuityads/acuityads_test.go @@ -3,9 +3,9 @@ package acuityads import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/acuityads/params_test.go b/adapters/acuityads/params_test.go index 892fe9a646d..e1a47669796 100644 --- a/adapters/acuityads/params_test.go +++ b/adapters/acuityads/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/acuityads/usersync.go b/adapters/acuityads/usersync.go index 7eedf78d229..90b610bb3cd 100644 --- a/adapters/acuityads/usersync.go +++ b/adapters/acuityads/usersync.go @@ -3,8 +3,8 @@ package acuityads import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAcuityAdsSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/acuityads/usersync_test.go b/adapters/acuityads/usersync_test.go index 1f57cea3b66..5c6f6a43677 100644 --- a/adapters/acuityads/usersync_test.go +++ b/adapters/acuityads/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adapterstest/adapter_test_util.go b/adapters/adapterstest/adapter_test_util.go index a5b3bb8b3f1..269eed087ed 100644 --- a/adapters/adapterstest/adapter_test_util.go +++ b/adapters/adapterstest/adapter_test_util.go @@ -8,7 +8,7 @@ import ( "net/http" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // OrtbMockService Represents a scaffolded OpenRTB service. diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go index a25a4f1905a..b8c33064fc4 100644 --- a/adapters/adapterstest/test_json.go +++ b/adapters/adapterstest/test_json.go @@ -7,8 +7,8 @@ import ( "regexp" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" "github.com/yudai/gojsondiff" "github.com/yudai/gojsondiff/formatter" diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go index e84df5b8cc7..bb3f9f4d8a3 100644 --- a/adapters/adform/adform.go +++ b/adapters/adform/adform.go @@ -13,14 +13,14 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index 558453e4030..14663c32112 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -10,17 +10,17 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adform/params_test.go b/adapters/adform/params_test.go index b392463f426..c4ffb79399e 100644 --- a/adapters/adform/params_test.go +++ b/adapters/adform/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/adform.json diff --git a/adapters/adform/usersync.go b/adapters/adform/usersync.go index 32b4e3a91c1..b98c1b6c0fc 100644 --- a/adapters/adform/usersync.go +++ b/adapters/adform/usersync.go @@ -3,8 +3,8 @@ package adform import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAdformSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/adform/usersync_test.go b/adapters/adform/usersync_test.go index 855506da2ed..3a507331e85 100644 --- a/adapters/adform/usersync_test.go +++ b/adapters/adform/usersync_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ) func TestAdformSyncer(t *testing.T) { diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go index b726d0c103a..59a3ba5b6a2 100644 --- a/adapters/adgeneration/adgeneration.go +++ b/adapters/adgeneration/adgeneration.go @@ -10,11 +10,11 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AdgenerationAdapter struct { diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go index f63c99cb92d..a4041a5a1d7 100644 --- a/adapters/adgeneration/adgeneration_test.go +++ b/adapters/adgeneration/adgeneration_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adgeneration/params_test.go b/adapters/adgeneration/params_test.go index 062d122ac08..11a0dfe97c5 100644 --- a/adapters/adgeneration/params_test.go +++ b/adapters/adgeneration/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adhese/adhese.go b/adapters/adhese/adhese.go index e13c03a101d..96a936c2276 100644 --- a/adapters/adhese/adhese.go +++ b/adapters/adhese/adhese.go @@ -10,13 +10,13 @@ import ( "strings" "text/template" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" ) type AdheseAdapter struct { diff --git a/adapters/adhese/adhese_test.go b/adapters/adhese/adhese_test.go index 3a9d28ee5e9..40b28887c20 100644 --- a/adapters/adhese/adhese_test.go +++ b/adapters/adhese/adhese_test.go @@ -3,9 +3,9 @@ package adhese import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adhese/params_test.go b/adapters/adhese/params_test.go index 45024749b2d..f9c3de6212e 100644 --- a/adapters/adhese/params_test.go +++ b/adapters/adhese/params_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adhese/utils.go b/adapters/adhese/utils.go index 9983be96d38..7b09ad02924 100644 --- a/adapters/adhese/utils.go +++ b/adapters/adhese/utils.go @@ -1,6 +1,6 @@ package adhese -import "github.com/mxmCherry/openrtb" +import "github.com/PubMatic-OpenWrap/openrtb" type AdheseOriginData struct { Priority string `json:"priority"` diff --git a/adapters/adkernel/adkernel.go b/adapters/adkernel/adkernel.go index 6bedeec612f..f483ba7ce49 100644 --- a/adapters/adkernel/adkernel.go +++ b/adapters/adkernel/adkernel.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type adkernelAdapter struct { diff --git a/adapters/adkernel/adkernel_test.go b/adapters/adkernel/adkernel_test.go index e9fcaef1b0e..a85769f5565 100644 --- a/adapters/adkernel/adkernel_test.go +++ b/adapters/adkernel/adkernel_test.go @@ -3,9 +3,9 @@ package adkernel import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adkernel/usersync.go b/adapters/adkernel/usersync.go index 2e2ebdb9fe3..7622e1da3b7 100644 --- a/adapters/adkernel/usersync.go +++ b/adapters/adkernel/usersync.go @@ -3,8 +3,8 @@ package adkernel import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const adkernelGDPRVendorID = uint16(14) diff --git a/adapters/adkernel/usersync_test.go b/adapters/adkernel/usersync_test.go index aeacf00b7f0..7230fcbab9c 100644 --- a/adapters/adkernel/usersync_test.go +++ b/adapters/adkernel/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adkernelAdn/adkernelAdn.go b/adapters/adkernelAdn/adkernelAdn.go index acd27c9e894..491bead4e8b 100644 --- a/adapters/adkernelAdn/adkernelAdn.go +++ b/adapters/adkernelAdn/adkernelAdn.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const defaultDomain string = "tag.adkernel.com" diff --git a/adapters/adkernelAdn/adkernelAdn_test.go b/adapters/adkernelAdn/adkernelAdn_test.go index 687f5a946c0..a4311d3e550 100644 --- a/adapters/adkernelAdn/adkernelAdn_test.go +++ b/adapters/adkernelAdn/adkernelAdn_test.go @@ -3,9 +3,9 @@ package adkernelAdn import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adkernelAdn/usersync.go b/adapters/adkernelAdn/usersync.go index 0bedac38e46..ab60edf2dc7 100644 --- a/adapters/adkernelAdn/usersync.go +++ b/adapters/adkernelAdn/usersync.go @@ -3,8 +3,8 @@ package adkernelAdn import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const adkernelGDPRVendorID = uint16(14) diff --git a/adapters/adkernelAdn/usersync_test.go b/adapters/adkernelAdn/usersync_test.go index 92d688e6117..9528579aa3d 100644 --- a/adapters/adkernelAdn/usersync_test.go +++ b/adapters/adkernelAdn/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adman/adman.go b/adapters/adman/adman.go index e0f67db816f..3c0342fe24f 100644 --- a/adapters/adman/adman.go +++ b/adapters/adman/adman.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // AdmanAdapter struct diff --git a/adapters/adman/adman_test.go b/adapters/adman/adman_test.go index ceee5f21bd6..5dc10df8dad 100644 --- a/adapters/adman/adman_test.go +++ b/adapters/adman/adman_test.go @@ -3,9 +3,9 @@ package adman import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adman/params_test.go b/adapters/adman/params_test.go index a80c2a44b8b..4cea67cc098 100644 --- a/adapters/adman/params_test.go +++ b/adapters/adman/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the adman schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/adman/usersync.go b/adapters/adman/usersync.go index aae6afcdfcd..f7edd8c5b70 100644 --- a/adapters/adman/usersync.go +++ b/adapters/adman/usersync.go @@ -3,8 +3,8 @@ package adman import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // NewAdmanSyncer returns adman syncer diff --git a/adapters/adman/usersync_test.go b/adapters/adman/usersync_test.go index 25da77db7ed..db67499e91d 100644 --- a/adapters/adman/usersync_test.go +++ b/adapters/adman/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/admixer/admixer.go b/adapters/admixer/admixer.go index eff93746df2..b16dc0073d4 100644 --- a/adapters/admixer/admixer.go +++ b/adapters/admixer/admixer.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AdmixerAdapter struct { diff --git a/adapters/admixer/admixer_test.go b/adapters/admixer/admixer_test.go index d994847c1ed..629d4df83cd 100644 --- a/adapters/admixer/admixer_test.go +++ b/adapters/admixer/admixer_test.go @@ -3,9 +3,9 @@ package admixer import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/admixer/params_test.go b/adapters/admixer/params_test.go index 71cccb6a3da..d25bdcecd68 100644 --- a/adapters/admixer/params_test.go +++ b/adapters/admixer/params_test.go @@ -2,7 +2,7 @@ package admixer import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/admixer/usersync.go b/adapters/admixer/usersync.go index 0a7f50ab79a..1df22cc276d 100644 --- a/adapters/admixer/usersync.go +++ b/adapters/admixer/usersync.go @@ -1,8 +1,8 @@ package admixer import ( - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "text/template" ) diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go index 79f023a236c..8a43b866804 100644 --- a/adapters/admixer/usersync_test.go +++ b/adapters/admixer/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adocean/adocean.go b/adapters/adocean/adocean.go index 43aa067b3a9..8310626fcec 100644 --- a/adapters/adocean/adocean.go +++ b/adapters/adocean/adocean.go @@ -13,12 +13,12 @@ import ( "strings" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const adapterVersion = "1.1.0" diff --git a/adapters/adocean/adocean_test.go b/adapters/adocean/adocean_test.go index 531204cdec3..b75de2a9235 100644 --- a/adapters/adocean/adocean_test.go +++ b/adapters/adocean/adocean_test.go @@ -3,9 +3,9 @@ package adocean import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adocean/params_test.go b/adapters/adocean/params_test.go index 1a88c4716e0..91e2fbdcb67 100644 --- a/adapters/adocean/params_test.go +++ b/adapters/adocean/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adocean/usersync.go b/adapters/adocean/usersync.go index 650e517a578..4bfe39e11e5 100644 --- a/adapters/adocean/usersync.go +++ b/adapters/adocean/usersync.go @@ -3,8 +3,8 @@ package adocean import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAdOceanSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/adocean/usersync_test.go b/adapters/adocean/usersync_test.go index 9ca81b98cb4..aa0bcb77e21 100644 --- a/adapters/adocean/usersync_test.go +++ b/adapters/adocean/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adoppler/adoppler.go b/adapters/adoppler/adoppler.go index adbb177d887..498bb4c7cc0 100644 --- a/adapters/adoppler/adoppler.go +++ b/adapters/adoppler/adoppler.go @@ -8,12 +8,12 @@ import ( "net/url" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const DefaultClient = "app" diff --git a/adapters/adoppler/adoppler_test.go b/adapters/adoppler/adoppler_test.go index fb5cb22bab5..eab0ac5708d 100644 --- a/adapters/adoppler/adoppler_test.go +++ b/adapters/adoppler/adoppler_test.go @@ -3,9 +3,9 @@ package adoppler import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adot/adot.go b/adapters/adot/adot.go index c58c4a1ee7a..fcbb5a2906d 100644 --- a/adapters/adot/adot.go +++ b/adapters/adot/adot.go @@ -3,11 +3,11 @@ package adot import ( "encoding/json" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "net/http" ) diff --git a/adapters/adot/adot_test.go b/adapters/adot/adot_test.go index fca6b303626..2e6e74861d4 100644 --- a/adapters/adot/adot_test.go +++ b/adapters/adot/adot_test.go @@ -2,11 +2,11 @@ package adot import ( "encoding/json" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" "testing" ) diff --git a/adapters/adot/params_test.go b/adapters/adot/params_test.go index 2f7b4b9af4e..2a6dc17d916 100644 --- a/adapters/adot/params_test.go +++ b/adapters/adot/params_test.go @@ -2,7 +2,7 @@ package adot import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/adpone/adpone.go b/adapters/adpone/adpone.go index 7907ceb1891..ec4cba75f87 100644 --- a/adapters/adpone/adpone.go +++ b/adapters/adpone/adpone.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ) // Builder builds a new instance of the Adpone adapter for the given bidder with the given config. diff --git a/adapters/adpone/adpone_test.go b/adapters/adpone/adpone_test.go index 78ad51ba095..69726e31d50 100644 --- a/adapters/adpone/adpone_test.go +++ b/adapters/adpone/adpone_test.go @@ -3,9 +3,9 @@ package adpone import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const testsDir = "adponetest" diff --git a/adapters/adpone/usersync.go b/adapters/adpone/usersync.go index 67d4c998275..63f616091e2 100644 --- a/adapters/adpone/usersync.go +++ b/adapters/adpone/usersync.go @@ -3,8 +3,8 @@ package adpone import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const adponeGDPRVendorID = uint16(799) diff --git a/adapters/adpone/usersync_test.go b/adapters/adpone/usersync_test.go index 87b4e9ae440..7bc528b8f36 100644 --- a/adapters/adpone/usersync_test.go +++ b/adapters/adpone/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go index 0db9218fdb4..053999fd5d1 100644 --- a/adapters/adprime/adprime.go +++ b/adapters/adprime/adprime.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) // AdprimeAdapter struct diff --git a/adapters/adprime/adprime_test.go b/adapters/adprime/adprime_test.go index ff12381d053..cfcf255a5cc 100644 --- a/adapters/adprime/adprime_test.go +++ b/adapters/adprime/adprime_test.go @@ -3,9 +3,9 @@ package adprime import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adprime/params_test.go b/adapters/adprime/params_test.go index 05adad5c4ff..bea13e32c13 100644 --- a/adapters/adprime/params_test.go +++ b/adapters/adprime/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the adprime schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/adtarget/adtarget.go b/adapters/adtarget/adtarget.go index 511918bb3df..070ede40feb 100644 --- a/adapters/adtarget/adtarget.go +++ b/adapters/adtarget/adtarget.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AdtargetAdapter struct { diff --git a/adapters/adtarget/adtarget_test.go b/adapters/adtarget/adtarget_test.go index ed21aef0828..bb20b40c286 100644 --- a/adapters/adtarget/adtarget_test.go +++ b/adapters/adtarget/adtarget_test.go @@ -3,9 +3,9 @@ package adtarget import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adtarget/params_test.go b/adapters/adtarget/params_test.go index b128d11c9cf..61ed4885512 100644 --- a/adapters/adtarget/params_test.go +++ b/adapters/adtarget/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/adtarget.json diff --git a/adapters/adtarget/usersync.go b/adapters/adtarget/usersync.go index 20bced25c72..93e57b173f6 100644 --- a/adapters/adtarget/usersync.go +++ b/adapters/adtarget/usersync.go @@ -3,8 +3,8 @@ package adtarget import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAdtargetSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/adtarget/usersync_test.go b/adapters/adtarget/usersync_test.go index ddba9e7a720..419a6cb037e 100644 --- a/adapters/adtarget/usersync_test.go +++ b/adapters/adtarget/usersync_test.go @@ -5,10 +5,10 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adtelligent/adtelligent.go b/adapters/adtelligent/adtelligent.go index fd501fde01f..7d8f6099fbf 100644 --- a/adapters/adtelligent/adtelligent.go +++ b/adapters/adtelligent/adtelligent.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AdtelligentAdapter struct { diff --git a/adapters/adtelligent/adtelligent_test.go b/adapters/adtelligent/adtelligent_test.go index 503b30b576a..6a104aa7463 100644 --- a/adapters/adtelligent/adtelligent_test.go +++ b/adapters/adtelligent/adtelligent_test.go @@ -3,9 +3,9 @@ package adtelligent import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adtelligent/params_test.go b/adapters/adtelligent/params_test.go index eb2aab0d437..8d8eb6d13b3 100644 --- a/adapters/adtelligent/params_test.go +++ b/adapters/adtelligent/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/adtelligent.json diff --git a/adapters/adtelligent/usersync.go b/adapters/adtelligent/usersync.go index 087b5bdd22d..387c65bb46d 100644 --- a/adapters/adtelligent/usersync.go +++ b/adapters/adtelligent/usersync.go @@ -3,8 +3,8 @@ package adtelligent import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAdtelligentSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/adtelligent/usersync_test.go b/adapters/adtelligent/usersync_test.go index fa157d226c5..7cc92eb4011 100644 --- a/adapters/adtelligent/usersync_test.go +++ b/adapters/adtelligent/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/advangelists/advangelists.go b/adapters/advangelists/advangelists.go index 95ddec0f452..249e3282481 100644 --- a/adapters/advangelists/advangelists.go +++ b/adapters/advangelists/advangelists.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AdvangelistsAdapter struct { diff --git a/adapters/advangelists/advangelists_test.go b/adapters/advangelists/advangelists_test.go index 4219c1a0237..49cca96a78a 100644 --- a/adapters/advangelists/advangelists_test.go +++ b/adapters/advangelists/advangelists_test.go @@ -3,9 +3,9 @@ package advangelists import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/advangelists/params_test.go b/adapters/advangelists/params_test.go index a58217a0ffd..2a94c782092 100644 --- a/adapters/advangelists/params_test.go +++ b/adapters/advangelists/params_test.go @@ -2,7 +2,7 @@ package advangelists import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/advangelists/usersync.go b/adapters/advangelists/usersync.go index 10c46d18a22..b1539d0093d 100644 --- a/adapters/advangelists/usersync.go +++ b/adapters/advangelists/usersync.go @@ -3,8 +3,8 @@ package advangelists import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAdvangelistsSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/advangelists/usersync_test.go b/adapters/advangelists/usersync_test.go index 2167d49fb05..5dae85b11a9 100644 --- a/adapters/advangelists/usersync_test.go +++ b/adapters/advangelists/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/aja/aja.go b/adapters/aja/aja.go index 07f9ad8bc96..afd9c6d7131 100644 --- a/adapters/aja/aja.go +++ b/adapters/aja/aja.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type AJAAdapter struct { diff --git a/adapters/aja/aja_test.go b/adapters/aja/aja_test.go index bab5419d889..d2d9d7fa7c1 100644 --- a/adapters/aja/aja_test.go +++ b/adapters/aja/aja_test.go @@ -3,9 +3,9 @@ package aja import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const testsBidderEndpoint = "https://localhost/bid/4" diff --git a/adapters/aja/usersync.go b/adapters/aja/usersync.go index c54405dbbd1..deddbabb1d9 100644 --- a/adapters/aja/usersync.go +++ b/adapters/aja/usersync.go @@ -3,8 +3,8 @@ package aja import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAJASyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/aja/usersync_test.go b/adapters/aja/usersync_test.go index 4b6c90ef141..bf03f47af19 100644 --- a/adapters/aja/usersync_test.go +++ b/adapters/aja/usersync_test.go @@ -4,10 +4,10 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/amx/amx.go b/adapters/amx/amx.go index a9dfd06f1b0..ddd0c0373da 100644 --- a/adapters/amx/amx.go +++ b/adapters/amx/amx.go @@ -7,11 +7,11 @@ import ( "net/url" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const vastImpressionFormat = "" diff --git a/adapters/amx/amx_test.go b/adapters/amx/amx_test.go index 8f05aec24d4..6fc850cb2cc 100644 --- a/adapters/amx/amx_test.go +++ b/adapters/amx/amx_test.go @@ -6,13 +6,13 @@ import ( "regexp" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ) const ( diff --git a/adapters/amx/params_test.go b/adapters/amx/params_test.go index 89e9a3adeb4..ef177644b21 100644 --- a/adapters/amx/params_test.go +++ b/adapters/amx/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/amx/usersync.go b/adapters/amx/usersync.go index 28e6ac0ed79..d9ff10df562 100644 --- a/adapters/amx/usersync.go +++ b/adapters/amx/usersync.go @@ -3,8 +3,8 @@ package amx import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // NewAMXSyncer produces an AMX RTB usersyncer diff --git a/adapters/amx/usersync_test.go b/adapters/amx/usersync_test.go index 20a47c33b69..e6020b27570 100644 --- a/adapters/amx/usersync_test.go +++ b/adapters/amx/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/applogy/applogy.go b/adapters/applogy/applogy.go index ebac02833ee..cdeafa0f426 100644 --- a/adapters/applogy/applogy.go +++ b/adapters/applogy/applogy.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ApplogyAdapter struct { diff --git a/adapters/applogy/applogy_test.go b/adapters/applogy/applogy_test.go index d86c5cacd68..63e99ed5895 100644 --- a/adapters/applogy/applogy_test.go +++ b/adapters/applogy/applogy_test.go @@ -3,9 +3,9 @@ package applogy import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/appnexus/appnexus.go b/adapters/appnexus/appnexus.go index 0a8146fdc41..7a5fcd6dce7 100644 --- a/adapters/appnexus/appnexus.go +++ b/adapters/appnexus/appnexus.go @@ -11,17 +11,17 @@ import ( "strconv" "strings" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const defaultPlatformID int = 5 diff --git a/adapters/appnexus/appnexus_test.go b/adapters/appnexus/appnexus_test.go index d453a779854..88b6652284b 100644 --- a/adapters/appnexus/appnexus_test.go +++ b/adapters/appnexus/appnexus_test.go @@ -13,17 +13,17 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/appnexus/params_test.go b/adapters/appnexus/params_test.go index f84cccc9a4c..c30f5cf3e2a 100644 --- a/adapters/appnexus/params_test.go +++ b/adapters/appnexus/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/appnexus.json diff --git a/adapters/appnexus/usersync.go b/adapters/appnexus/usersync.go index 22f46f1e723..16ffdfa3338 100644 --- a/adapters/appnexus/usersync.go +++ b/adapters/appnexus/usersync.go @@ -3,8 +3,8 @@ package appnexus import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAppnexusSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/appnexus/usersync_test.go b/adapters/appnexus/usersync_test.go index 24b9eede9d6..6796ce13b96 100644 --- a/adapters/appnexus/usersync_test.go +++ b/adapters/appnexus/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go index 909a03a2bcf..a74c51d9ecb 100644 --- a/adapters/audienceNetwork/facebook.go +++ b/adapters/audienceNetwork/facebook.go @@ -10,14 +10,14 @@ import ( "net/http" "strings" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/maputil" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/util/maputil" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" ) var supportedBannerHeights = map[uint64]bool{ diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go index a2e17b71ca8..596529dbb9a 100644 --- a/adapters/audienceNetwork/facebook_test.go +++ b/adapters/audienceNetwork/facebook_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/audienceNetwork/usersync.go b/adapters/audienceNetwork/usersync.go index 45c1281d0ae..6b8ba3ba7a3 100644 --- a/adapters/audienceNetwork/usersync.go +++ b/adapters/audienceNetwork/usersync.go @@ -3,8 +3,8 @@ package audienceNetwork import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewFacebookSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/audienceNetwork/usersync_test.go b/adapters/audienceNetwork/usersync_test.go index c3836e4f154..e11fb194dec 100644 --- a/adapters/audienceNetwork/usersync_test.go +++ b/adapters/audienceNetwork/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/avocet/avocet.go b/adapters/avocet/avocet.go index dac7faaa4b3..ef2314e7ebb 100644 --- a/adapters/avocet/avocet.go +++ b/adapters/avocet/avocet.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // AvocetAdapter implements a adapters.Bidder compatible with the Avocet advertising platform. diff --git a/adapters/avocet/avocet_test.go b/adapters/avocet/avocet_test.go index e4a7c5f44fa..f669e34492f 100644 --- a/adapters/avocet/avocet_test.go +++ b/adapters/avocet/avocet_test.go @@ -6,12 +6,12 @@ import ( "reflect" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/avocet/usersync.go b/adapters/avocet/usersync.go index f1075ab3c52..ec4f25dd952 100644 --- a/adapters/avocet/usersync.go +++ b/adapters/avocet/usersync.go @@ -3,8 +3,8 @@ package avocet import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewAvocetSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go index 3df39b77fce..12b7901cc90 100644 --- a/adapters/avocet/usersync_test.go +++ b/adapters/avocet/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go index 1f81eda03de..8aca4ca0427 100644 --- a/adapters/beachfront/beachfront.go +++ b/adapters/beachfront/beachfront.go @@ -9,11 +9,11 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const Seat = "beachfront" diff --git a/adapters/beachfront/beachfront_test.go b/adapters/beachfront/beachfront_test.go index 1c1c4ff4469..aace3224534 100644 --- a/adapters/beachfront/beachfront_test.go +++ b/adapters/beachfront/beachfront_test.go @@ -3,9 +3,9 @@ package beachfront import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/beachfront/params_test.go b/adapters/beachfront/params_test.go index ebc0a768900..982bd96c609 100644 --- a/adapters/beachfront/params_test.go +++ b/adapters/beachfront/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/beachfront/usersync.go b/adapters/beachfront/usersync.go index a6502331bee..f355697f4e0 100644 --- a/adapters/beachfront/usersync.go +++ b/adapters/beachfront/usersync.go @@ -3,8 +3,8 @@ package beachfront import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) var VENDOR_ID uint16 = 335 diff --git a/adapters/beachfront/usersync_test.go b/adapters/beachfront/usersync_test.go index db4d825eb5a..58a072d01b3 100644 --- a/adapters/beachfront/usersync_test.go +++ b/adapters/beachfront/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/beintoo/beintoo.go b/adapters/beintoo/beintoo.go index 5f22bfa7742..9d704c2265e 100644 --- a/adapters/beintoo/beintoo.go +++ b/adapters/beintoo/beintoo.go @@ -7,11 +7,11 @@ import ( "net/url" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type BeintooAdapter struct { diff --git a/adapters/beintoo/beintoo_test.go b/adapters/beintoo/beintoo_test.go index 181b9562577..477d4a5483a 100644 --- a/adapters/beintoo/beintoo_test.go +++ b/adapters/beintoo/beintoo_test.go @@ -3,9 +3,9 @@ package beintoo import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/beintoo/params_test.go b/adapters/beintoo/params_test.go index b92b2a108dd..dca0111f9a5 100644 --- a/adapters/beintoo/params_test.go +++ b/adapters/beintoo/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/beintoo/usersync.go b/adapters/beintoo/usersync.go index a225a2b15da..1693c082b7f 100644 --- a/adapters/beintoo/usersync.go +++ b/adapters/beintoo/usersync.go @@ -3,8 +3,8 @@ package beintoo import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewBeintooSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/beintoo/usersync_test.go b/adapters/beintoo/usersync_test.go index 880d6a84cee..f0597d8f574 100644 --- a/adapters/beintoo/usersync_test.go +++ b/adapters/beintoo/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/between/between.go b/adapters/between/between.go index f8106bdd113..038aa4b8b10 100644 --- a/adapters/between/between.go +++ b/adapters/between/between.go @@ -8,12 +8,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type BetweenAdapter struct { diff --git a/adapters/between/between_test.go b/adapters/between/between_test.go index 6471b37b187..ee89d663260 100644 --- a/adapters/between/between_test.go +++ b/adapters/between/between_test.go @@ -3,9 +3,9 @@ package between import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/between/params_test.go b/adapters/between/params_test.go index 857718690d3..599fb95bfdf 100644 --- a/adapters/between/params_test.go +++ b/adapters/between/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/between.json diff --git a/adapters/between/usersync.go b/adapters/between/usersync.go index a34521fe438..bf6a1d32c18 100644 --- a/adapters/between/usersync.go +++ b/adapters/between/usersync.go @@ -3,8 +3,8 @@ package between import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // NewBetweenSyncer returns "between" syncer diff --git a/adapters/between/usersync_test.go b/adapters/between/usersync_test.go index 6470a9c441c..17a51e70c95 100644 --- a/adapters/between/usersync_test.go +++ b/adapters/between/usersync_test.go @@ -1,7 +1,7 @@ package between import ( - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" "testing" "text/template" diff --git a/adapters/bidder.go b/adapters/bidder.go index 507a065bac7..e80312919c9 100644 --- a/adapters/bidder.go +++ b/adapters/bidder.go @@ -5,10 +5,10 @@ import ( "encoding/json" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Bidder describes how to connect to external demand. diff --git a/adapters/brightroll/brightroll.go b/adapters/brightroll/brightroll.go index 0e3fcb0669c..d1f5868275e 100644 --- a/adapters/brightroll/brightroll.go +++ b/adapters/brightroll/brightroll.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type BrightrollAdapter struct { diff --git a/adapters/brightroll/brightroll_test.go b/adapters/brightroll/brightroll_test.go index a41a9e792c5..c25c57eddf0 100644 --- a/adapters/brightroll/brightroll_test.go +++ b/adapters/brightroll/brightroll_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestEmptyConfig(t *testing.T) { diff --git a/adapters/brightroll/params_test.go b/adapters/brightroll/params_test.go index beac822f8f0..6c65b8ce3ef 100644 --- a/adapters/brightroll/params_test.go +++ b/adapters/brightroll/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/brightroll.json diff --git a/adapters/brightroll/usersync.go b/adapters/brightroll/usersync.go index b33cc5a2943..74729f70025 100644 --- a/adapters/brightroll/usersync.go +++ b/adapters/brightroll/usersync.go @@ -3,8 +3,8 @@ package brightroll import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewBrightrollSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/brightroll/usersync_test.go b/adapters/brightroll/usersync_test.go index a5d47e35e56..1ca0325495e 100644 --- a/adapters/brightroll/usersync_test.go +++ b/adapters/brightroll/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/colossus/colossus.go b/adapters/colossus/colossus.go index 4a5360ce122..941ca4ce3b3 100644 --- a/adapters/colossus/colossus.go +++ b/adapters/colossus/colossus.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) type ColossusAdapter struct { diff --git a/adapters/colossus/colossus_test.go b/adapters/colossus/colossus_test.go index 7baa423af70..25d107a544b 100644 --- a/adapters/colossus/colossus_test.go +++ b/adapters/colossus/colossus_test.go @@ -3,9 +3,9 @@ package colossus import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/colossus/params_test.go b/adapters/colossus/params_test.go index 2883de2f53e..c223209e5a1 100644 --- a/adapters/colossus/params_test.go +++ b/adapters/colossus/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the colossus schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/colossus/usersync.go b/adapters/colossus/usersync.go index a4e82ee3bde..519dcaba86e 100644 --- a/adapters/colossus/usersync.go +++ b/adapters/colossus/usersync.go @@ -3,8 +3,8 @@ package colossus import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // NewColossusSyncer returns colossus syncer diff --git a/adapters/colossus/usersync_test.go b/adapters/colossus/usersync_test.go index 52eb6389b1c..5bbbdc089fc 100644 --- a/adapters/colossus/usersync_test.go +++ b/adapters/colossus/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/connectad/connectad.go b/adapters/connectad/connectad.go index 024dabcee98..7731e0fd51b 100644 --- a/adapters/connectad/connectad.go +++ b/adapters/connectad/connectad.go @@ -7,11 +7,11 @@ import ( "net/url" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ConnectAdAdapter struct { diff --git a/adapters/connectad/connectad_test.go b/adapters/connectad/connectad_test.go index 36005219ce8..58984e909c4 100644 --- a/adapters/connectad/connectad_test.go +++ b/adapters/connectad/connectad_test.go @@ -3,9 +3,9 @@ package connectad import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/connectad/params_test.go b/adapters/connectad/params_test.go index 6d55b1ce7d9..c8c407f8404 100644 --- a/adapters/connectad/params_test.go +++ b/adapters/connectad/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/connectad/usersync.go b/adapters/connectad/usersync.go index 5661cb5d9d8..ccd2d52cf40 100644 --- a/adapters/connectad/usersync.go +++ b/adapters/connectad/usersync.go @@ -3,8 +3,8 @@ package connectad import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewConnectAdSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/connectad/usersync_test.go b/adapters/connectad/usersync_test.go index c4b4962b9f6..c7b5c7c3fd2 100644 --- a/adapters/connectad/usersync_test.go +++ b/adapters/connectad/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/consumable/adtypes.go b/adapters/consumable/adtypes.go index 5eb3d3b369e..f04f8822f7a 100644 --- a/adapters/consumable/adtypes.go +++ b/adapters/consumable/adtypes.go @@ -1,7 +1,7 @@ package consumable import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "strconv" ) diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go index 15511c0ebb7..e6aea9c55f0 100644 --- a/adapters/consumable/consumable.go +++ b/adapters/consumable/consumable.go @@ -8,12 +8,12 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ) type ConsumableAdapter struct { diff --git a/adapters/consumable/consumable_test.go b/adapters/consumable/consumable_test.go index 56e1d626c7b..63f5d92972e 100644 --- a/adapters/consumable/consumable_test.go +++ b/adapters/consumable/consumable_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/consumable/params_test.go b/adapters/consumable/params_test.go index 42de5cb9ca8..9f922796bc2 100644 --- a/adapters/consumable/params_test.go +++ b/adapters/consumable/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/consumable.json diff --git a/adapters/consumable/usersync.go b/adapters/consumable/usersync.go index 0e0938c7b39..720b0bea78b 100644 --- a/adapters/consumable/usersync.go +++ b/adapters/consumable/usersync.go @@ -3,8 +3,8 @@ package consumable import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) var VENDOR_ID uint16 = 591 diff --git a/adapters/consumable/usersync_test.go b/adapters/consumable/usersync_test.go index ef71c0b18c7..1aeb2eb8c8d 100644 --- a/adapters/consumable/usersync_test.go +++ b/adapters/consumable/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/conversant/cnvr_legacy.go b/adapters/conversant/cnvr_legacy.go index 4672ee156b4..d330698e9d2 100644 --- a/adapters/conversant/cnvr_legacy.go +++ b/adapters/conversant/cnvr_legacy.go @@ -8,10 +8,10 @@ import ( "io/ioutil" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/conversant/cnvr_legacy_test.go b/adapters/conversant/cnvr_legacy_test.go index 712f85f4404..cfa5bc7b8ba 100644 --- a/adapters/conversant/cnvr_legacy_test.go +++ b/adapters/conversant/cnvr_legacy_test.go @@ -11,12 +11,12 @@ import ( "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // Constants diff --git a/adapters/conversant/conversant.go b/adapters/conversant/conversant.go index 248aa200d8e..fe4e6602868 100644 --- a/adapters/conversant/conversant.go +++ b/adapters/conversant/conversant.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ConversantAdapter struct { diff --git a/adapters/conversant/conversant_test.go b/adapters/conversant/conversant_test.go index 55b316b473f..2de1facf8a2 100644 --- a/adapters/conversant/conversant_test.go +++ b/adapters/conversant/conversant_test.go @@ -3,9 +3,9 @@ package conversant import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/conversant/usersync.go b/adapters/conversant/usersync.go index c2676df6620..3fe0a45cef7 100644 --- a/adapters/conversant/usersync.go +++ b/adapters/conversant/usersync.go @@ -3,8 +3,8 @@ package conversant import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewConversantSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/conversant/usersync_test.go b/adapters/conversant/usersync_test.go index 16affbd1d32..ef05454b036 100644 --- a/adapters/conversant/usersync_test.go +++ b/adapters/conversant/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/cpmstar/cpmstar.go b/adapters/cpmstar/cpmstar.go index f3fbdf70516..d7bbb57635f 100644 --- a/adapters/cpmstar/cpmstar.go +++ b/adapters/cpmstar/cpmstar.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type Adapter struct { diff --git a/adapters/cpmstar/cpmstar_test.go b/adapters/cpmstar/cpmstar_test.go index c10dfbe1a59..8d214e4ee4a 100644 --- a/adapters/cpmstar/cpmstar_test.go +++ b/adapters/cpmstar/cpmstar_test.go @@ -3,9 +3,9 @@ package cpmstar import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/cpmstar/params_test.go b/adapters/cpmstar/params_test.go index cee471a8322..dec3f342a9d 100644 --- a/adapters/cpmstar/params_test.go +++ b/adapters/cpmstar/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/cpmstar.json diff --git a/adapters/cpmstar/usersync.go b/adapters/cpmstar/usersync.go index 9c864e24ce3..91d3c89c4ab 100644 --- a/adapters/cpmstar/usersync.go +++ b/adapters/cpmstar/usersync.go @@ -3,8 +3,8 @@ package cpmstar import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) //NewCpmstarSyncer : diff --git a/adapters/cpmstar/usersync_test.go b/adapters/cpmstar/usersync_test.go index dae55e6302e..cd04838a859 100644 --- a/adapters/cpmstar/usersync_test.go +++ b/adapters/cpmstar/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/datablocks/datablocks.go b/adapters/datablocks/datablocks.go index 56ac8f681d7..84380184c78 100644 --- a/adapters/datablocks/datablocks.go +++ b/adapters/datablocks/datablocks.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type DatablocksAdapter struct { diff --git a/adapters/datablocks/datablocks_test.go b/adapters/datablocks/datablocks_test.go index 553c8edf8da..1ee06b9349e 100644 --- a/adapters/datablocks/datablocks_test.go +++ b/adapters/datablocks/datablocks_test.go @@ -3,9 +3,9 @@ package datablocks import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/datablocks/usersync.go b/adapters/datablocks/usersync.go index 2b47b259e39..3993215bcad 100644 --- a/adapters/datablocks/usersync.go +++ b/adapters/datablocks/usersync.go @@ -3,8 +3,8 @@ package datablocks import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const datablocksGDPRVendorID = uint16(0) diff --git a/adapters/datablocks/usersync_test.go b/adapters/datablocks/usersync_test.go index a7518e9b226..3ab7ae1b477 100644 --- a/adapters/datablocks/usersync_test.go +++ b/adapters/datablocks/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/decenterads/decenterads.go b/adapters/decenterads/decenterads.go index 5719bf1e4b3..c0ba2eb90cc 100644 --- a/adapters/decenterads/decenterads.go +++ b/adapters/decenterads/decenterads.go @@ -7,11 +7,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type adapter struct { diff --git a/adapters/decenterads/decenterads_test.go b/adapters/decenterads/decenterads_test.go index ca86e89187c..eed77d7a9a3 100644 --- a/adapters/decenterads/decenterads_test.go +++ b/adapters/decenterads/decenterads_test.go @@ -3,9 +3,9 @@ package decenterads import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/decenterads/params_test.go b/adapters/decenterads/params_test.go index 3d3708be789..fcf5e94f3c2 100644 --- a/adapters/decenterads/params_test.go +++ b/adapters/decenterads/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the decenterads schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/deepintent/deepintent.go b/adapters/deepintent/deepintent.go index 1ddaa1563c9..5a9b30c9cda 100644 --- a/adapters/deepintent/deepintent.go +++ b/adapters/deepintent/deepintent.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const displayManager string = "di_prebid" diff --git a/adapters/deepintent/deepintent_test.go b/adapters/deepintent/deepintent_test.go index cdf158e6d28..a0dbf836400 100644 --- a/adapters/deepintent/deepintent_test.go +++ b/adapters/deepintent/deepintent_test.go @@ -3,10 +3,10 @@ package deepintent import ( "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/deepintent/params_test.go b/adapters/deepintent/params_test.go index 8f37e5a9bd6..87f80dfde74 100644 --- a/adapters/deepintent/params_test.go +++ b/adapters/deepintent/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the deepintent schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/deepintent/usersync.go b/adapters/deepintent/usersync.go index 9e803df6d9d..8ab615e3222 100644 --- a/adapters/deepintent/usersync.go +++ b/adapters/deepintent/usersync.go @@ -3,8 +3,8 @@ package deepintent import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) // NewDeepintentSyncer returns deepintent syncer diff --git a/adapters/deepintent/usersync_test.go b/adapters/deepintent/usersync_test.go index 06a221ca2fd..1e0174ab60b 100644 --- a/adapters/deepintent/usersync_test.go +++ b/adapters/deepintent/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go index c66c6bf38fa..aa6163ad0f4 100644 --- a/adapters/dmx/dmx.go +++ b/adapters/dmx/dmx.go @@ -8,11 +8,11 @@ import ( "net/url" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type DmxAdapter struct { diff --git a/adapters/dmx/dmx_test.go b/adapters/dmx/dmx_test.go index 80498de6e04..0649cb8a714 100644 --- a/adapters/dmx/dmx_test.go +++ b/adapters/dmx/dmx_test.go @@ -5,12 +5,12 @@ import ( "strings" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ) var ( diff --git a/adapters/dmx/params_test.go b/adapters/dmx/params_test.go index 0e5250b173e..4d9da7863e1 100644 --- a/adapters/dmx/params_test.go +++ b/adapters/dmx/params_test.go @@ -2,7 +2,7 @@ package dmx import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/dmx/usersync.go b/adapters/dmx/usersync.go index 98e56234fa6..07c70ff9155 100644 --- a/adapters/dmx/usersync.go +++ b/adapters/dmx/usersync.go @@ -3,8 +3,8 @@ package dmx import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewDmxSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/dmx/usersync_test.go b/adapters/dmx/usersync_test.go index e4e3c7d8e55..2aa66575f8a 100644 --- a/adapters/dmx/usersync_test.go +++ b/adapters/dmx/usersync_test.go @@ -1,7 +1,7 @@ package dmx import ( - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "testing" "text/template" diff --git a/adapters/emx_digital/emx_digital.go b/adapters/emx_digital/emx_digital.go index e87f0681075..dcebfdcbead 100644 --- a/adapters/emx_digital/emx_digital.go +++ b/adapters/emx_digital/emx_digital.go @@ -9,11 +9,11 @@ import ( "strings" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type EmxDigitalAdapter struct { diff --git a/adapters/emx_digital/emx_digital_test.go b/adapters/emx_digital/emx_digital_test.go index bb7567e8b17..3e0e52e2845 100644 --- a/adapters/emx_digital/emx_digital_test.go +++ b/adapters/emx_digital/emx_digital_test.go @@ -3,10 +3,10 @@ package emx_digital import ( "testing" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/emx_digital/params_test.go b/adapters/emx_digital/params_test.go index 49ad9eb1a9c..9a6ff5cf73f 100644 --- a/adapters/emx_digital/params_test.go +++ b/adapters/emx_digital/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/emx_digital/usersync.go b/adapters/emx_digital/usersync.go index a453955b22e..38b6377f317 100644 --- a/adapters/emx_digital/usersync.go +++ b/adapters/emx_digital/usersync.go @@ -3,8 +3,8 @@ package emx_digital import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewEMXDigitalSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/emx_digital/usersync_test.go b/adapters/emx_digital/usersync_test.go index 59d66d87808..15bdd738fdb 100644 --- a/adapters/emx_digital/usersync_test.go +++ b/adapters/emx_digital/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/engagebdr/engagebdr.go b/adapters/engagebdr/engagebdr.go index 16d20afa611..a2401cd79b5 100644 --- a/adapters/engagebdr/engagebdr.go +++ b/adapters/engagebdr/engagebdr.go @@ -4,14 +4,14 @@ import ( "encoding/json" "net/http" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ) type EngageBDRAdapter struct { diff --git a/adapters/engagebdr/engagebdr_test.go b/adapters/engagebdr/engagebdr_test.go index 97e6a8e974f..699ef446e39 100644 --- a/adapters/engagebdr/engagebdr_test.go +++ b/adapters/engagebdr/engagebdr_test.go @@ -3,9 +3,9 @@ package engagebdr import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/engagebdr/params_test.go b/adapters/engagebdr/params_test.go index c797d04ecc8..b6c887eac66 100644 --- a/adapters/engagebdr/params_test.go +++ b/adapters/engagebdr/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/engagebdr/usersync.go b/adapters/engagebdr/usersync.go index ae4047aa89c..99b6a1224c0 100644 --- a/adapters/engagebdr/usersync.go +++ b/adapters/engagebdr/usersync.go @@ -1,8 +1,8 @@ package engagebdr import ( - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "text/template" ) diff --git a/adapters/engagebdr/usersync_test.go b/adapters/engagebdr/usersync_test.go index 3a6c179addf..588f1f97a79 100644 --- a/adapters/engagebdr/usersync_test.go +++ b/adapters/engagebdr/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go index 8d0f7e8b662..062dcff4d61 100644 --- a/adapters/eplanning/eplanning.go +++ b/adapters/eplanning/eplanning.go @@ -11,11 +11,11 @@ import ( "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "strconv" ) diff --git a/adapters/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go index 70e108d5e0a..bf43278845e 100644 --- a/adapters/eplanning/eplanning_test.go +++ b/adapters/eplanning/eplanning_test.go @@ -3,10 +3,10 @@ package eplanning import ( "testing" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/eplanning/usersync.go b/adapters/eplanning/usersync.go index faa7fa82a19..4f9f30aec3a 100644 --- a/adapters/eplanning/usersync.go +++ b/adapters/eplanning/usersync.go @@ -3,8 +3,8 @@ package eplanning import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewEPlanningSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/eplanning/usersync_test.go b/adapters/eplanning/usersync_test.go index 85770689024..dd0bf15239a 100644 --- a/adapters/eplanning/usersync_test.go +++ b/adapters/eplanning/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/gamma/gamma.go b/adapters/gamma/gamma.go index f8681fb93c0..47f5775ff8e 100644 --- a/adapters/gamma/gamma.go +++ b/adapters/gamma/gamma.go @@ -7,11 +7,11 @@ import ( "net/url" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type GammaAdapter struct { diff --git a/adapters/gamma/gamma_test.go b/adapters/gamma/gamma_test.go index 2741e425fae..fb648cd4154 100644 --- a/adapters/gamma/gamma_test.go +++ b/adapters/gamma/gamma_test.go @@ -3,9 +3,9 @@ package gamma import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/gamma/params_test.go b/adapters/gamma/params_test.go index 3329545a264..5e096cbf849 100644 --- a/adapters/gamma/params_test.go +++ b/adapters/gamma/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/gamma.json diff --git a/adapters/gamma/usersync.go b/adapters/gamma/usersync.go index f19c522cebc..884468fe8ae 100644 --- a/adapters/gamma/usersync.go +++ b/adapters/gamma/usersync.go @@ -3,8 +3,8 @@ package gamma import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewGammaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/gamma/usersync_test.go b/adapters/gamma/usersync_test.go index 4278f9abd36..04a88e5ede8 100644 --- a/adapters/gamma/usersync_test.go +++ b/adapters/gamma/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/gamoshi/gamoshi.go b/adapters/gamoshi/gamoshi.go index c0791e66ddc..a6ab5febd3d 100644 --- a/adapters/gamoshi/gamoshi.go +++ b/adapters/gamoshi/gamoshi.go @@ -6,12 +6,12 @@ import ( "net/http" "strconv" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) type GamoshiAdapter struct { diff --git a/adapters/gamoshi/gamoshi_test.go b/adapters/gamoshi/gamoshi_test.go index d8834c45123..dc8c6091e51 100644 --- a/adapters/gamoshi/gamoshi_test.go +++ b/adapters/gamoshi/gamoshi_test.go @@ -3,9 +3,9 @@ package gamoshi import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamplesWithConfiguredURI(t *testing.T) { diff --git a/adapters/gamoshi/params_test.go b/adapters/gamoshi/params_test.go index 29a1864b9ae..d33740ee515 100644 --- a/adapters/gamoshi/params_test.go +++ b/adapters/gamoshi/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/gamoshi.json diff --git a/adapters/gamoshi/usersync.go b/adapters/gamoshi/usersync.go index 6b7c43dd6a2..73101ada257 100644 --- a/adapters/gamoshi/usersync.go +++ b/adapters/gamoshi/usersync.go @@ -3,8 +3,8 @@ package gamoshi import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewGamoshiSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/gamoshi/usersync_test.go b/adapters/gamoshi/usersync_test.go index 43dc88a4953..08ddd5341c1 100644 --- a/adapters/gamoshi/usersync_test.go +++ b/adapters/gamoshi/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" "github.com/stretchr/testify/assert" ) diff --git a/adapters/grid/grid.go b/adapters/grid/grid.go index e70b1ddd632..955ead6b6c8 100644 --- a/adapters/grid/grid.go +++ b/adapters/grid/grid.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type GridAdapter struct { diff --git a/adapters/grid/grid_test.go b/adapters/grid/grid_test.go index 1f9baaba640..8438bd28908 100644 --- a/adapters/grid/grid_test.go +++ b/adapters/grid/grid_test.go @@ -3,9 +3,9 @@ package grid import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/grid/usersync.go b/adapters/grid/usersync.go index afdc5db763c..52d25405b44 100644 --- a/adapters/grid/usersync.go +++ b/adapters/grid/usersync.go @@ -3,8 +3,8 @@ package grid import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewGridSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/grid/usersync_test.go b/adapters/grid/usersync_test.go index 99730b5deb4..40cb443fe97 100644 --- a/adapters/grid/usersync_test.go +++ b/adapters/grid/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/gumgum/gumgum.go b/adapters/gumgum/gumgum.go index 9b26f8f8099..acb08ea3c00 100644 --- a/adapters/gumgum/gumgum.go +++ b/adapters/gumgum/gumgum.go @@ -7,11 +7,11 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // GumGumAdapter implements Bidder interface. diff --git a/adapters/gumgum/gumgum_test.go b/adapters/gumgum/gumgum_test.go index b097042b6d3..fc2d1059ac8 100644 --- a/adapters/gumgum/gumgum_test.go +++ b/adapters/gumgum/gumgum_test.go @@ -3,9 +3,9 @@ package gumgum import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/gumgum/params_test.go b/adapters/gumgum/params_test.go index 4cb6f019197..087c9136031 100644 --- a/adapters/gumgum/params_test.go +++ b/adapters/gumgum/params_test.go @@ -2,7 +2,7 @@ package gumgum import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/gumgum/usersync.go b/adapters/gumgum/usersync.go index 5d29c7dceb2..b56b9d73c3c 100644 --- a/adapters/gumgum/usersync.go +++ b/adapters/gumgum/usersync.go @@ -3,8 +3,8 @@ package gumgum import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewGumGumSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/gumgum/usersync_test.go b/adapters/gumgum/usersync_test.go index 9c6dc420600..47301f9cef0 100644 --- a/adapters/gumgum/usersync_test.go +++ b/adapters/gumgum/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go index 0d5c043ef60..acda1b06d5a 100644 --- a/adapters/improvedigital/improvedigital.go +++ b/adapters/improvedigital/improvedigital.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ImprovedigitalAdapter struct { diff --git a/adapters/improvedigital/improvedigital_test.go b/adapters/improvedigital/improvedigital_test.go index 7a45faacff4..b4530882cc2 100644 --- a/adapters/improvedigital/improvedigital_test.go +++ b/adapters/improvedigital/improvedigital_test.go @@ -3,9 +3,9 @@ package improvedigital import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/improvedigital/params_test.go b/adapters/improvedigital/params_test.go index 13bdd807560..f603479793f 100644 --- a/adapters/improvedigital/params_test.go +++ b/adapters/improvedigital/params_test.go @@ -2,7 +2,7 @@ package improvedigital import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/improvedigital/usersync.go b/adapters/improvedigital/usersync.go index 72c3ddafd49..9b5f7e89872 100644 --- a/adapters/improvedigital/usersync.go +++ b/adapters/improvedigital/usersync.go @@ -3,8 +3,8 @@ package improvedigital import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewImprovedigitalSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/improvedigital/usersync_test.go b/adapters/improvedigital/usersync_test.go index 35ea89cf894..91c6cd91865 100644 --- a/adapters/improvedigital/usersync_test.go +++ b/adapters/improvedigital/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/info.go b/adapters/info.go index 19897ac7031..63c66fc9862 100644 --- a/adapters/info.go +++ b/adapters/info.go @@ -5,11 +5,11 @@ import ( "io/ioutil" "strings" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" yaml "gopkg.in/yaml.v2" ) diff --git a/adapters/info_test.go b/adapters/info_test.go index ce0f88696db..2d1f84cb760 100644 --- a/adapters/info_test.go +++ b/adapters/info_test.go @@ -5,11 +5,11 @@ import ( "strings" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/inmobi/inmobi.go b/adapters/inmobi/inmobi.go index 5d978ec7935..411988fc4fa 100644 --- a/adapters/inmobi/inmobi.go +++ b/adapters/inmobi/inmobi.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type InMobiAdapter struct { diff --git a/adapters/inmobi/inmobi_test.go b/adapters/inmobi/inmobi_test.go index 0b662684f72..bfe5d10bcef 100644 --- a/adapters/inmobi/inmobi_test.go +++ b/adapters/inmobi/inmobi_test.go @@ -3,9 +3,9 @@ package inmobi import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/invibes/invibes.go b/adapters/invibes/invibes.go index 31124bd108f..358b16266ec 100644 --- a/adapters/invibes/invibes.go +++ b/adapters/invibes/invibes.go @@ -9,13 +9,13 @@ import ( "strings" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const adapterVersion = "prebid_1.0.0" diff --git a/adapters/invibes/invibes_test.go b/adapters/invibes/invibes_test.go index 73d6ec0bec5..47071d4768f 100644 --- a/adapters/invibes/invibes_test.go +++ b/adapters/invibes/invibes_test.go @@ -3,9 +3,9 @@ package invibes import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/invibes/params_test.go b/adapters/invibes/params_test.go index 8c3a26b4eac..84d1d0493a4 100644 --- a/adapters/invibes/params_test.go +++ b/adapters/invibes/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/invibes/usersync.go b/adapters/invibes/usersync.go index 468e604c1a6..c334f8ffa0b 100644 --- a/adapters/invibes/usersync.go +++ b/adapters/invibes/usersync.go @@ -3,8 +3,8 @@ package invibes import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewInvibesSyncer(urlTemplate *template.Template) usersync.Usersyncer { diff --git a/adapters/invibes/usersync_test.go b/adapters/invibes/usersync_test.go index 3f715f6bdfe..e575daf241b 100644 --- a/adapters/invibes/usersync_test.go +++ b/adapters/invibes/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go index 96cd988de54..e0a571aa8cc 100644 --- a/adapters/ix/ix.go +++ b/adapters/ix/ix.go @@ -10,12 +10,12 @@ import ( "golang.org/x/net/context/ctxhttp" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" ) type IxAdapter struct { diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go index 8a75e6852b7..aaee4ba7694 100644 --- a/adapters/ix/ix_test.go +++ b/adapters/ix/ix_test.go @@ -11,12 +11,12 @@ import ( "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" ) const endpoint string = "http://host/endpoint" diff --git a/adapters/ix/usersync.go b/adapters/ix/usersync.go index 6f3558949e0..dcb4d646489 100644 --- a/adapters/ix/usersync.go +++ b/adapters/ix/usersync.go @@ -3,8 +3,8 @@ package ix import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewIxSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/ix/usersync_test.go b/adapters/ix/usersync_test.go index 10f91da1abe..142ee6ac027 100644 --- a/adapters/ix/usersync_test.go +++ b/adapters/ix/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/kidoz/kidoz.go b/adapters/kidoz/kidoz.go index fda6869e12f..755ffd24d63 100644 --- a/adapters/kidoz/kidoz.go +++ b/adapters/kidoz/kidoz.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type KidozAdapter struct { diff --git a/adapters/kidoz/kidoz_test.go b/adapters/kidoz/kidoz_test.go index 5830f60ddb5..b3365ac70a9 100644 --- a/adapters/kidoz/kidoz_test.go +++ b/adapters/kidoz/kidoz_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/kidoz/params_test.go b/adapters/kidoz/params_test.go index 073d7382d68..43c5a68d69d 100644 --- a/adapters/kidoz/params_test.go +++ b/adapters/kidoz/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/krushmedia/krushmedia.go b/adapters/krushmedia/krushmedia.go index d05b6e7979a..1f2e6b636c6 100644 --- a/adapters/krushmedia/krushmedia.go +++ b/adapters/krushmedia/krushmedia.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type KrushmediaAdapter struct { diff --git a/adapters/krushmedia/krushmedia_test.go b/adapters/krushmedia/krushmedia_test.go index 7bdea503569..0fc5c93fed2 100644 --- a/adapters/krushmedia/krushmedia_test.go +++ b/adapters/krushmedia/krushmedia_test.go @@ -3,9 +3,9 @@ package krushmedia import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/krushmedia/params_test.go b/adapters/krushmedia/params_test.go index 26daa56e10b..0f912513b2c 100644 --- a/adapters/krushmedia/params_test.go +++ b/adapters/krushmedia/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/krushmedia/usersync.go b/adapters/krushmedia/usersync.go index 5dc1471fb9f..0a4f664e56f 100644 --- a/adapters/krushmedia/usersync.go +++ b/adapters/krushmedia/usersync.go @@ -3,8 +3,8 @@ package krushmedia import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewKrushmediaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/krushmedia/usersync_test.go b/adapters/krushmedia/usersync_test.go index b58f2f1bc4e..a5908b8061d 100644 --- a/adapters/krushmedia/usersync_test.go +++ b/adapters/krushmedia/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/kubient/kubient.go b/adapters/kubient/kubient.go index a995586533a..fd6c4ef13e8 100644 --- a/adapters/kubient/kubient.go +++ b/adapters/kubient/kubient.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ) // Builder builds a new instance of the Kubient adapter for the given bidder with the given config. diff --git a/adapters/kubient/kubient_test.go b/adapters/kubient/kubient_test.go index 19eb3e8ff13..e7f8a9ee2fc 100644 --- a/adapters/kubient/kubient_test.go +++ b/adapters/kubient/kubient_test.go @@ -3,9 +3,9 @@ package kubient import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/legacy.go b/adapters/legacy.go index 8b2221fe0ca..c3fde16d764 100644 --- a/adapters/legacy.go +++ b/adapters/legacy.go @@ -6,8 +6,8 @@ import ( "net/http" "time" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/server/ssl" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" ) // This file contains some deprecated, legacy types. diff --git a/adapters/lifestreet/lifestreet.go b/adapters/lifestreet/lifestreet.go index 4dbd7baed62..ca9b9688943 100644 --- a/adapters/lifestreet/lifestreet.go +++ b/adapters/lifestreet/lifestreet.go @@ -9,12 +9,12 @@ import ( "net/http" "strings" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/lifestreet/lifestreet_test.go b/adapters/lifestreet/lifestreet_test.go index 412333c3714..39712192da6 100644 --- a/adapters/lifestreet/lifestreet_test.go +++ b/adapters/lifestreet/lifestreet_test.go @@ -10,15 +10,15 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" ) type lsTagInfo struct { diff --git a/adapters/lifestreet/usersync.go b/adapters/lifestreet/usersync.go index 15ffde4db74..4f18854e54a 100644 --- a/adapters/lifestreet/usersync.go +++ b/adapters/lifestreet/usersync.go @@ -3,8 +3,8 @@ package lifestreet import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewLifestreetSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/lifestreet/usersync_test.go b/adapters/lifestreet/usersync_test.go index 8fee09358e8..134af2f5b88 100644 --- a/adapters/lifestreet/usersync_test.go +++ b/adapters/lifestreet/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/lockerdome/lockerdome.go b/adapters/lockerdome/lockerdome.go index a81313c6e80..ec9ebe8c281 100644 --- a/adapters/lockerdome/lockerdome.go +++ b/adapters/lockerdome/lockerdome.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const unexpectedStatusCodeMessage = "Unexpected status code: %d. Run with request.debug = 1 for more info" diff --git a/adapters/lockerdome/lockerdome_test.go b/adapters/lockerdome/lockerdome_test.go index 6ac495d5d7c..ca3da099eaa 100644 --- a/adapters/lockerdome/lockerdome_test.go +++ b/adapters/lockerdome/lockerdome_test.go @@ -3,9 +3,9 @@ package lockerdome import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/lockerdome/params_test.go b/adapters/lockerdome/params_test.go index 815246571e3..61ee259a437 100644 --- a/adapters/lockerdome/params_test.go +++ b/adapters/lockerdome/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file tests static/bidder-params/lockerdome.json diff --git a/adapters/lockerdome/usersync.go b/adapters/lockerdome/usersync.go index fb01f8e2d99..67c4e15dd2d 100644 --- a/adapters/lockerdome/usersync.go +++ b/adapters/lockerdome/usersync.go @@ -3,8 +3,8 @@ package lockerdome import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewLockerDomeSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/lockerdome/usersync_test.go b/adapters/lockerdome/usersync_test.go index b5dea2261b6..acfa788e5f7 100644 --- a/adapters/lockerdome/usersync_test.go +++ b/adapters/lockerdome/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/logicad/logicad.go b/adapters/logicad/logicad.go index 30d9dd5df4c..dd316321381 100644 --- a/adapters/logicad/logicad.go +++ b/adapters/logicad/logicad.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type LogicadAdapter struct { diff --git a/adapters/logicad/logicad_test.go b/adapters/logicad/logicad_test.go index 820aad9751d..5da8e56e6c2 100644 --- a/adapters/logicad/logicad_test.go +++ b/adapters/logicad/logicad_test.go @@ -3,9 +3,9 @@ package logicad import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/logicad/params_test.go b/adapters/logicad/params_test.go index eb34452811b..8b5d296bd1f 100644 --- a/adapters/logicad/params_test.go +++ b/adapters/logicad/params_test.go @@ -2,7 +2,7 @@ package logicad import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/logicad/usersync.go b/adapters/logicad/usersync.go index d26a197b0a1..34d29a25543 100644 --- a/adapters/logicad/usersync.go +++ b/adapters/logicad/usersync.go @@ -3,8 +3,8 @@ package logicad import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewLogicadSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/logicad/usersync_test.go b/adapters/logicad/usersync_test.go index 89d6207d348..aeb029d3380 100644 --- a/adapters/logicad/usersync_test.go +++ b/adapters/logicad/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/lunamedia/lunamedia.go b/adapters/lunamedia/lunamedia.go index 289d062c8bb..c5e0ef8a3ee 100644 --- a/adapters/lunamedia/lunamedia.go +++ b/adapters/lunamedia/lunamedia.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type LunaMediaAdapter struct { diff --git a/adapters/lunamedia/lunamedia_test.go b/adapters/lunamedia/lunamedia_test.go index 4149060c809..6d0952cdd9c 100644 --- a/adapters/lunamedia/lunamedia_test.go +++ b/adapters/lunamedia/lunamedia_test.go @@ -3,9 +3,9 @@ package lunamedia import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/lunamedia/params_test.go b/adapters/lunamedia/params_test.go index b4faeea1f77..2f21ea45510 100644 --- a/adapters/lunamedia/params_test.go +++ b/adapters/lunamedia/params_test.go @@ -2,7 +2,7 @@ package lunamedia import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/lunamedia/usersync.go b/adapters/lunamedia/usersync.go index 7ad54e384a1..194c4b77dbe 100644 --- a/adapters/lunamedia/usersync.go +++ b/adapters/lunamedia/usersync.go @@ -3,8 +3,8 @@ package lunamedia import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewLunaMediaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/lunamedia/usersync_test.go b/adapters/lunamedia/usersync_test.go index c9fe2032d2c..3a549aec5f7 100644 --- a/adapters/lunamedia/usersync_test.go +++ b/adapters/lunamedia/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/marsmedia/marsmedia.go b/adapters/marsmedia/marsmedia.go index eb1e8da0858..f1cffac2274 100644 --- a/adapters/marsmedia/marsmedia.go +++ b/adapters/marsmedia/marsmedia.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type MarsmediaAdapter struct { diff --git a/adapters/marsmedia/marsmedia_test.go b/adapters/marsmedia/marsmedia_test.go index ab87bf773a4..03e46312ca2 100644 --- a/adapters/marsmedia/marsmedia_test.go +++ b/adapters/marsmedia/marsmedia_test.go @@ -3,9 +3,9 @@ package marsmedia import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/marsmedia/params_test.go b/adapters/marsmedia/params_test.go index 43cd49c2ed3..2e3b483824d 100644 --- a/adapters/marsmedia/params_test.go +++ b/adapters/marsmedia/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/marsmedia.json diff --git a/adapters/marsmedia/usersync.go b/adapters/marsmedia/usersync.go index 50996f325ac..63d06d9dcc5 100644 --- a/adapters/marsmedia/usersync.go +++ b/adapters/marsmedia/usersync.go @@ -3,8 +3,8 @@ package marsmedia import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewMarsmediaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/marsmedia/usersync_test.go b/adapters/marsmedia/usersync_test.go index f019c014516..cc4f0b819f9 100644 --- a/adapters/marsmedia/usersync_test.go +++ b/adapters/marsmedia/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/mediafuse/usersync.go b/adapters/mediafuse/usersync.go index 5381bf3606d..ca299c724f6 100644 --- a/adapters/mediafuse/usersync.go +++ b/adapters/mediafuse/usersync.go @@ -3,8 +3,8 @@ package mediafuse import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewMediafuseSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/mediafuse/usersync_test.go b/adapters/mediafuse/usersync_test.go index 30b5b535b12..95e6a7cf93f 100644 --- a/adapters/mediafuse/usersync_test.go +++ b/adapters/mediafuse/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/mgid/mgid.go b/adapters/mgid/mgid.go index d6ba175d336..9754ba8088e 100644 --- a/adapters/mgid/mgid.go +++ b/adapters/mgid/mgid.go @@ -6,11 +6,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type MgidAdapter struct { diff --git a/adapters/mgid/mgid_test.go b/adapters/mgid/mgid_test.go index 7d30045168d..11f5596ed9a 100644 --- a/adapters/mgid/mgid_test.go +++ b/adapters/mgid/mgid_test.go @@ -3,9 +3,9 @@ package mgid import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/mgid/usersync.go b/adapters/mgid/usersync.go index fbdb95f01fc..3eb77025d4d 100644 --- a/adapters/mgid/usersync.go +++ b/adapters/mgid/usersync.go @@ -3,8 +3,8 @@ package mgid import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewMgidSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/mgid/usersync_test.go b/adapters/mgid/usersync_test.go index 2ce634eeac2..b918dabfe0b 100644 --- a/adapters/mgid/usersync_test.go +++ b/adapters/mgid/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/mobfoxpb/mobfoxpb.go b/adapters/mobfoxpb/mobfoxpb.go index 5369c082c52..42ac3dc02d4 100644 --- a/adapters/mobfoxpb/mobfoxpb.go +++ b/adapters/mobfoxpb/mobfoxpb.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) type adapter struct { diff --git a/adapters/mobfoxpb/mobfoxpb_test.go b/adapters/mobfoxpb/mobfoxpb_test.go index 271d30a97af..23bdb28118c 100644 --- a/adapters/mobfoxpb/mobfoxpb_test.go +++ b/adapters/mobfoxpb/mobfoxpb_test.go @@ -3,9 +3,9 @@ package mobfoxpb import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/mobfoxpb/params_test.go b/adapters/mobfoxpb/params_test.go index 59b9ec383c8..ddd738ece2b 100644 --- a/adapters/mobfoxpb/params_test.go +++ b/adapters/mobfoxpb/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the mobfoxpb schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/mobilefuse/mobilefuse.go b/adapters/mobilefuse/mobilefuse.go index 76c18e27007..4e545ec1adc 100644 --- a/adapters/mobilefuse/mobilefuse.go +++ b/adapters/mobilefuse/mobilefuse.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type MobileFuseAdapter struct { diff --git a/adapters/mobilefuse/mobilefuse_test.go b/adapters/mobilefuse/mobilefuse_test.go index 3abe627fab0..52d2ab20768 100644 --- a/adapters/mobilefuse/mobilefuse_test.go +++ b/adapters/mobilefuse/mobilefuse_test.go @@ -3,9 +3,9 @@ package mobilefuse import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/mobilefuse/params_test.go b/adapters/mobilefuse/params_test.go index dbfd8894e70..6d98f656983 100644 --- a/adapters/mobilefuse/params_test.go +++ b/adapters/mobilefuse/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(test *testing.T) { diff --git a/adapters/nanointeractive/nanointeractive.go b/adapters/nanointeractive/nanointeractive.go index 82574d8ba0e..9dfee06eb42 100644 --- a/adapters/nanointeractive/nanointeractive.go +++ b/adapters/nanointeractive/nanointeractive.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type NanoInteractiveAdapter struct { diff --git a/adapters/nanointeractive/nanointeractive_test.go b/adapters/nanointeractive/nanointeractive_test.go index d0955511f8b..79caf9cf417 100644 --- a/adapters/nanointeractive/nanointeractive_test.go +++ b/adapters/nanointeractive/nanointeractive_test.go @@ -3,9 +3,9 @@ package nanointeractive import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/nanointeractive/params_test.go b/adapters/nanointeractive/params_test.go index b290f3d94b1..309d19b5128 100644 --- a/adapters/nanointeractive/params_test.go +++ b/adapters/nanointeractive/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/nanointeractive.json diff --git a/adapters/nanointeractive/usersync.go b/adapters/nanointeractive/usersync.go index e6227436fb2..180e2c53520 100644 --- a/adapters/nanointeractive/usersync.go +++ b/adapters/nanointeractive/usersync.go @@ -3,8 +3,8 @@ package nanointeractive import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewNanoInteractiveSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go index fa78664928f..44040756316 100644 --- a/adapters/nanointeractive/usersync_test.go +++ b/adapters/nanointeractive/usersync_test.go @@ -4,10 +4,10 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/ninthdecimal/ninthdecimal.go b/adapters/ninthdecimal/ninthdecimal.go index 03095ee6c44..5cb631db6f1 100755 --- a/adapters/ninthdecimal/ninthdecimal.go +++ b/adapters/ninthdecimal/ninthdecimal.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type NinthDecimalAdapter struct { diff --git a/adapters/ninthdecimal/ninthdecimal_test.go b/adapters/ninthdecimal/ninthdecimal_test.go index ccb8114f8a9..9d82f38783b 100755 --- a/adapters/ninthdecimal/ninthdecimal_test.go +++ b/adapters/ninthdecimal/ninthdecimal_test.go @@ -3,9 +3,9 @@ package ninthdecimal import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/ninthdecimal/params_test.go b/adapters/ninthdecimal/params_test.go index 8d3ef3d706f..cc06088caff 100755 --- a/adapters/ninthdecimal/params_test.go +++ b/adapters/ninthdecimal/params_test.go @@ -2,7 +2,7 @@ package ninthdecimal import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/ninthdecimal/usersync.go b/adapters/ninthdecimal/usersync.go index 7a8d029cfa9..4ea91d031cb 100755 --- a/adapters/ninthdecimal/usersync.go +++ b/adapters/ninthdecimal/usersync.go @@ -3,8 +3,8 @@ package ninthdecimal import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewNinthDecimalSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/ninthdecimal/usersync_test.go b/adapters/ninthdecimal/usersync_test.go index ee121434f29..ded5bf0dbf4 100755 --- a/adapters/ninthdecimal/usersync_test.go +++ b/adapters/ninthdecimal/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/nobid/nobid.go b/adapters/nobid/nobid.go index 875a7260e80..77d1a7f806f 100644 --- a/adapters/nobid/nobid.go +++ b/adapters/nobid/nobid.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // NoBidAdapter - NoBid Adapter definition diff --git a/adapters/nobid/nobid_test.go b/adapters/nobid/nobid_test.go index 674d189d661..420c57f36bc 100644 --- a/adapters/nobid/nobid_test.go +++ b/adapters/nobid/nobid_test.go @@ -3,9 +3,9 @@ package nobid import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/nobid/params_test.go b/adapters/nobid/params_test.go index 75d69943d35..f8c0533c300 100644 --- a/adapters/nobid/params_test.go +++ b/adapters/nobid/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/nobid/usersync.go b/adapters/nobid/usersync.go index c21413f59e7..3b36e59fa3d 100644 --- a/adapters/nobid/usersync.go +++ b/adapters/nobid/usersync.go @@ -3,8 +3,8 @@ package nobid import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewNoBidSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/nobid/usersync_test.go b/adapters/nobid/usersync_test.go index bc55d130509..a59d70a4f91 100644 --- a/adapters/nobid/usersync_test.go +++ b/adapters/nobid/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/openrtb_util.go b/adapters/openrtb_util.go index 55064ea7d07..88023920b8d 100644 --- a/adapters/openrtb_util.go +++ b/adapters/openrtb_util.go @@ -3,10 +3,10 @@ package adapters import ( "encoding/json" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) func min(x, y int) int { diff --git a/adapters/openrtb_util_test.go b/adapters/openrtb_util_test.go index 2cf67c22537..fbb9ab57991 100644 --- a/adapters/openrtb_util_test.go +++ b/adapters/openrtb_util_test.go @@ -5,9 +5,9 @@ import ( "encoding/json" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/stretchr/testify/assert" ) diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go index b6640b1f5e9..21fbd37e99f 100644 --- a/adapters/openx/openx.go +++ b/adapters/openx/openx.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const hbconfig = "hb_pbs_1.0.0" diff --git a/adapters/openx/openx_test.go b/adapters/openx/openx_test.go index 81d2ff227ec..5eec4feb41a 100644 --- a/adapters/openx/openx_test.go +++ b/adapters/openx/openx_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/openx/params_test.go b/adapters/openx/params_test.go index b7ea970ab1f..87ce08fc733 100644 --- a/adapters/openx/params_test.go +++ b/adapters/openx/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/openx.json diff --git a/adapters/openx/usersync.go b/adapters/openx/usersync.go index bb4b328ce62..f557e5e4095 100644 --- a/adapters/openx/usersync.go +++ b/adapters/openx/usersync.go @@ -3,8 +3,8 @@ package openx import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewOpenxSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/openx/usersync_test.go b/adapters/openx/usersync_test.go index cdbb7da8b95..7bb30399069 100644 --- a/adapters/openx/usersync_test.go +++ b/adapters/openx/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/orbidder/orbidder.go b/adapters/orbidder/orbidder.go index e26ad4efc0c..4e13fc6f50e 100644 --- a/adapters/orbidder/orbidder.go +++ b/adapters/orbidder/orbidder.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type OrbidderAdapter struct { diff --git a/adapters/orbidder/orbidder_test.go b/adapters/orbidder/orbidder_test.go index 0eaed23a971..4e9fc2f8bb7 100644 --- a/adapters/orbidder/orbidder_test.go +++ b/adapters/orbidder/orbidder_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/orbidder/params_test.go b/adapters/orbidder/params_test.go index 19c4ed8d9d4..98fcf0217db 100644 --- a/adapters/orbidder/params_test.go +++ b/adapters/orbidder/params_test.go @@ -2,7 +2,7 @@ package orbidder import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/pubmatic/params_test.go b/adapters/pubmatic/params_test.go index c8a300b9910..9615fb978e6 100644 --- a/adapters/pubmatic/params_test.go +++ b/adapters/pubmatic/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/pubmatic.json diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go index a4c4e222210..01add99c128 100644 --- a/adapters/pubmatic/pubmatic.go +++ b/adapters/pubmatic/pubmatic.go @@ -11,13 +11,13 @@ import ( "strconv" "strings" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go index 8ed2ccd391c..8c59a0a9a6c 100644 --- a/adapters/pubmatic/pubmatic_test.go +++ b/adapters/pubmatic/pubmatic_test.go @@ -12,14 +12,14 @@ import ( "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/pubmatic/usersync.go b/adapters/pubmatic/usersync.go index 7b4d8e86b50..f35470c0ad9 100644 --- a/adapters/pubmatic/usersync.go +++ b/adapters/pubmatic/usersync.go @@ -3,8 +3,8 @@ package pubmatic import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewPubmaticSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/pubmatic/usersync_test.go b/adapters/pubmatic/usersync_test.go index d6cd9f78af7..e32650ac543 100644 --- a/adapters/pubmatic/usersync_test.go +++ b/adapters/pubmatic/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/pubnative/pubnative.go b/adapters/pubnative/pubnative.go index 7d393f8ade9..36edceee53f 100644 --- a/adapters/pubnative/pubnative.go +++ b/adapters/pubnative/pubnative.go @@ -7,11 +7,11 @@ import ( "net/url" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type PubnativeAdapter struct { diff --git a/adapters/pubnative/pubnative_test.go b/adapters/pubnative/pubnative_test.go index 6955b85f5de..484315cac5e 100644 --- a/adapters/pubnative/pubnative_test.go +++ b/adapters/pubnative/pubnative_test.go @@ -3,9 +3,9 @@ package pubnative import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/pulsepoint/params_test.go b/adapters/pulsepoint/params_test.go index ac2b314b96f..f6d6baf9f06 100644 --- a/adapters/pulsepoint/params_test.go +++ b/adapters/pulsepoint/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/pulsepoint/pulsepoint.go b/adapters/pulsepoint/pulsepoint.go index b07a2cba66f..8025e9b29c8 100644 --- a/adapters/pulsepoint/pulsepoint.go +++ b/adapters/pulsepoint/pulsepoint.go @@ -11,12 +11,12 @@ import ( "io/ioutil" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/pulsepoint/pulsepoint_test.go b/adapters/pulsepoint/pulsepoint_test.go index 33023d0500a..7e9b51016ea 100644 --- a/adapters/pulsepoint/pulsepoint_test.go +++ b/adapters/pulsepoint/pulsepoint_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "bytes" "context" @@ -16,11 +16,11 @@ import ( "net/http/httptest" "time" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/pulsepoint/usersync.go b/adapters/pulsepoint/usersync.go index 58de835be28..4c7d5ca63c8 100644 --- a/adapters/pulsepoint/usersync.go +++ b/adapters/pulsepoint/usersync.go @@ -3,8 +3,8 @@ package pulsepoint import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewPulsepointSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/pulsepoint/usersync_test.go b/adapters/pulsepoint/usersync_test.go index e1680fd22ba..8dd32564930 100644 --- a/adapters/pulsepoint/usersync_test.go +++ b/adapters/pulsepoint/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/revcontent/revcontent.go b/adapters/revcontent/revcontent.go index 5a34f3ef199..c6a7ff9d827 100644 --- a/adapters/revcontent/revcontent.go +++ b/adapters/revcontent/revcontent.go @@ -3,11 +3,11 @@ package revcontent import ( "encoding/json" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "net/http" ) diff --git a/adapters/revcontent/revcontent_test.go b/adapters/revcontent/revcontent_test.go index 836ef138eb7..d7f83cdb2aa 100644 --- a/adapters/revcontent/revcontent_test.go +++ b/adapters/revcontent/revcontent_test.go @@ -3,9 +3,9 @@ package revcontent import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/rhythmone/params_test.go b/adapters/rhythmone/params_test.go index 7d8cad47d53..00eacf15082 100644 --- a/adapters/rhythmone/params_test.go +++ b/adapters/rhythmone/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/rhythmone/rhythmone.go b/adapters/rhythmone/rhythmone.go index 7dd94a494c8..e2fc9aa8f0d 100644 --- a/adapters/rhythmone/rhythmone.go +++ b/adapters/rhythmone/rhythmone.go @@ -6,11 +6,11 @@ import ( "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type RhythmoneAdapter struct { diff --git a/adapters/rhythmone/rhythmone_test.go b/adapters/rhythmone/rhythmone_test.go index c0c0a891ef4..e2419f33568 100644 --- a/adapters/rhythmone/rhythmone_test.go +++ b/adapters/rhythmone/rhythmone_test.go @@ -3,9 +3,9 @@ package rhythmone import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/rhythmone/usersync.go b/adapters/rhythmone/usersync.go index 0f7db388e5c..534c60dd4bc 100644 --- a/adapters/rhythmone/usersync.go +++ b/adapters/rhythmone/usersync.go @@ -3,8 +3,8 @@ package rhythmone import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewRhythmoneSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/rhythmone/usersync_test.go b/adapters/rhythmone/usersync_test.go index 85ecba2a8ab..1c725626a46 100644 --- a/adapters/rhythmone/usersync_test.go +++ b/adapters/rhythmone/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/rtbhouse/rtbhouse.go b/adapters/rtbhouse/rtbhouse.go index 1f86d4f365b..26f85d4adad 100644 --- a/adapters/rtbhouse/rtbhouse.go +++ b/adapters/rtbhouse/rtbhouse.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Builder builds a new instance of the RTBHouse adapter for the given bidder with the given config. diff --git a/adapters/rtbhouse/rtbhouse_test.go b/adapters/rtbhouse/rtbhouse_test.go index bae9a636038..15dfd1fc9b2 100644 --- a/adapters/rtbhouse/rtbhouse_test.go +++ b/adapters/rtbhouse/rtbhouse_test.go @@ -3,9 +3,9 @@ package rtbhouse import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const testsDir = "rtbhousetest" diff --git a/adapters/rtbhouse/usersync.go b/adapters/rtbhouse/usersync.go index 3e38d67e593..97e673124aa 100644 --- a/adapters/rtbhouse/usersync.go +++ b/adapters/rtbhouse/usersync.go @@ -3,8 +3,8 @@ package rtbhouse import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const rtbHouseGDPRVendorID = uint16(16) diff --git a/adapters/rtbhouse/usersync_test.go b/adapters/rtbhouse/usersync_test.go index bb139f7a2e5..7b1d198b74d 100644 --- a/adapters/rtbhouse/usersync_test.go +++ b/adapters/rtbhouse/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 91211a61d4e..7fe88640cb0 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -10,16 +10,16 @@ import ( "net/url" "strings" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const badvLimitSize = 50 diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index 41e37f41126..54953b213b1 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "github.com/prebid/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" "io/ioutil" "net/http" "net/http/httptest" @@ -12,19 +12,19 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "fmt" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/rubicon/usersync.go b/adapters/rubicon/usersync.go index 08d98825a1e..9c86024771e 100644 --- a/adapters/rubicon/usersync.go +++ b/adapters/rubicon/usersync.go @@ -3,8 +3,8 @@ package rubicon import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewRubiconSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/rubicon/usersync_test.go b/adapters/rubicon/usersync_test.go index 646ebff2dc4..2fd53d6b62b 100644 --- a/adapters/rubicon/usersync_test.go +++ b/adapters/rubicon/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 36af79c4534..738d382a938 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -9,11 +9,11 @@ import ( "strconv" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ) const defaultTmax = 10000 // 10 sec diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go index 402e8365dd0..f6d43971f4e 100644 --- a/adapters/sharethrough/butler_test.go +++ b/adapters/sharethrough/butler_test.go @@ -9,10 +9,10 @@ import ( "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sharethrough/params_test.go b/adapters/sharethrough/params_test.go index 416f459341d..e4e659f4420 100644 --- a/adapters/sharethrough/params_test.go +++ b/adapters/sharethrough/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index a6ef4736fdf..63958d029f1 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -5,11 +5,11 @@ import ( "net/http" "regexp" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const supplyId = "FGMrCMMc" diff --git a/adapters/sharethrough/sharethrough_test.go b/adapters/sharethrough/sharethrough_test.go index 9fca80e03ee..0e31a90bac4 100644 --- a/adapters/sharethrough/sharethrough_test.go +++ b/adapters/sharethrough/sharethrough_test.go @@ -6,11 +6,11 @@ import ( "regexp" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sharethrough/usersync.go b/adapters/sharethrough/usersync.go index a951fcd6a0a..7d5d6f135a8 100644 --- a/adapters/sharethrough/usersync.go +++ b/adapters/sharethrough/usersync.go @@ -1,8 +1,8 @@ package sharethrough import ( - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "text/template" ) diff --git a/adapters/sharethrough/usersync_test.go b/adapters/sharethrough/usersync_test.go index 4802329554e..c48a6d51f8e 100644 --- a/adapters/sharethrough/usersync_test.go +++ b/adapters/sharethrough/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 76d8b7a1f76..2dc22615aa7 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -5,9 +5,9 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" "html/template" "net" "net/url" diff --git a/adapters/sharethrough/utils_test.go b/adapters/sharethrough/utils_test.go index fffc52f6140..aa5ae58cc5c 100644 --- a/adapters/sharethrough/utils_test.go +++ b/adapters/sharethrough/utils_test.go @@ -4,8 +4,8 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" "regexp" "testing" diff --git a/adapters/silvermob/params_test.go b/adapters/silvermob/params_test.go index 13009f6a08b..fbfb5f3d097 100644 --- a/adapters/silvermob/params_test.go +++ b/adapters/silvermob/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // TestValidParams makes sure that the silvermob schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/silvermob/silvermob.go b/adapters/silvermob/silvermob.go index 2e31e51c0ad..0e79f44369f 100644 --- a/adapters/silvermob/silvermob.go +++ b/adapters/silvermob/silvermob.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type SilverMobAdapter struct { diff --git a/adapters/silvermob/silvermob_test.go b/adapters/silvermob/silvermob_test.go index 795d58fd834..8045542c7c5 100644 --- a/adapters/silvermob/silvermob_test.go +++ b/adapters/silvermob/silvermob_test.go @@ -3,9 +3,9 @@ package silvermob import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/smaato/params_test.go b/adapters/smaato/params_test.go index 6c71cbe75c6..80993dc5739 100644 --- a/adapters/smaato/params_test.go +++ b/adapters/smaato/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file intends to test static/bidder-params/smaato.json diff --git a/adapters/smaato/smaato.go b/adapters/smaato/smaato.go index 1d5b29ab2c0..2873f1311a4 100644 --- a/adapters/smaato/smaato.go +++ b/adapters/smaato/smaato.go @@ -6,12 +6,12 @@ import ( "net/http" "strings" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) const clientVersion = "prebid_server_0.1" diff --git a/adapters/smaato/smaato_test.go b/adapters/smaato/smaato_test.go index c7c4a65017f..5fdea31693f 100644 --- a/adapters/smaato/smaato_test.go +++ b/adapters/smaato/smaato_test.go @@ -3,9 +3,9 @@ package smaato import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/smartadserver/params_test.go b/adapters/smartadserver/params_test.go index b424cbe0a1d..363886d96b1 100644 --- a/adapters/smartadserver/params_test.go +++ b/adapters/smartadserver/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/smartadserver.json diff --git a/adapters/smartadserver/smartadserver.go b/adapters/smartadserver/smartadserver.go index e2735d3bece..1231ab677ec 100644 --- a/adapters/smartadserver/smartadserver.go +++ b/adapters/smartadserver/smartadserver.go @@ -8,11 +8,11 @@ import ( "path" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type SmartAdserverAdapter struct { diff --git a/adapters/smartadserver/smartadserver_test.go b/adapters/smartadserver/smartadserver_test.go index 978be336471..e6cf5e12b55 100644 --- a/adapters/smartadserver/smartadserver_test.go +++ b/adapters/smartadserver/smartadserver_test.go @@ -3,9 +3,9 @@ package smartadserver import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/smartadserver/usersync.go b/adapters/smartadserver/usersync.go index 95b305ff227..cc155966c12 100644 --- a/adapters/smartadserver/usersync.go +++ b/adapters/smartadserver/usersync.go @@ -3,8 +3,8 @@ package smartadserver import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSmartadserverSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/smartadserver/usersync_test.go b/adapters/smartadserver/usersync_test.go index c4e6660693f..aeb34dd69f9 100644 --- a/adapters/smartadserver/usersync_test.go +++ b/adapters/smartadserver/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/smartrtb/smartrtb.go b/adapters/smartrtb/smartrtb.go index 364c52a7437..0613bb6815f 100644 --- a/adapters/smartrtb/smartrtb.go +++ b/adapters/smartrtb/smartrtb.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Base adapter structure. diff --git a/adapters/smartrtb/smartrtb_test.go b/adapters/smartrtb/smartrtb_test.go index ef5fce668f4..1d592dfc6d8 100644 --- a/adapters/smartrtb/smartrtb_test.go +++ b/adapters/smartrtb/smartrtb_test.go @@ -3,9 +3,9 @@ package smartrtb import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/smartrtb/usersync.go b/adapters/smartrtb/usersync.go index 2f7b1dc3339..1148dcc1584 100644 --- a/adapters/smartrtb/usersync.go +++ b/adapters/smartrtb/usersync.go @@ -3,8 +3,8 @@ package smartrtb import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSmartRTBSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/smartrtb/usersync_test.go b/adapters/smartrtb/usersync_test.go index ae3ae5dc007..6fa5c837296 100644 --- a/adapters/smartrtb/usersync_test.go +++ b/adapters/smartrtb/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/smartyads/params_test.go b/adapters/smartyads/params_test.go index 3aa5c0e837d..048989a2c40 100644 --- a/adapters/smartyads/params_test.go +++ b/adapters/smartyads/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/smartyads/smartyads.go b/adapters/smartyads/smartyads.go index ba727b4730c..93a94a74568 100644 --- a/adapters/smartyads/smartyads.go +++ b/adapters/smartyads/smartyads.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type SmartyAdsAdapter struct { diff --git a/adapters/smartyads/smartyads_test.go b/adapters/smartyads/smartyads_test.go index 4ea20e98c74..dae8e2a0726 100644 --- a/adapters/smartyads/smartyads_test.go +++ b/adapters/smartyads/smartyads_test.go @@ -3,9 +3,9 @@ package smartyads import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/smartyads/usersync.go b/adapters/smartyads/usersync.go index 839621e9927..d394fb730b0 100644 --- a/adapters/smartyads/usersync.go +++ b/adapters/smartyads/usersync.go @@ -3,8 +3,8 @@ package smartyads import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSmartyAdsSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/smartyads/usersync_test.go b/adapters/smartyads/usersync_test.go index ed7fc07b594..6ea8aa81846 100644 --- a/adapters/smartyads/usersync_test.go +++ b/adapters/smartyads/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/somoaudience/params_test.go b/adapters/somoaudience/params_test.go index 2cbb2b1f51a..b74725aac21 100644 --- a/adapters/somoaudience/params_test.go +++ b/adapters/somoaudience/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/somoaudience.json diff --git a/adapters/somoaudience/somoaudience.go b/adapters/somoaudience/somoaudience.go index 2bcb0282fe6..2e096dfe1b3 100644 --- a/adapters/somoaudience/somoaudience.go +++ b/adapters/somoaudience/somoaudience.go @@ -6,12 +6,12 @@ import ( "net/http" "strconv" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) const hbconfig = "hb_pbs_1.0.0" diff --git a/adapters/somoaudience/somoaudience_test.go b/adapters/somoaudience/somoaudience_test.go index 07a1d2b3b65..3f3ce8c6ca9 100644 --- a/adapters/somoaudience/somoaudience_test.go +++ b/adapters/somoaudience/somoaudience_test.go @@ -3,9 +3,9 @@ package somoaudience import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/somoaudience/usersync.go b/adapters/somoaudience/usersync.go index e1e7e2ef21d..2d0b715d669 100644 --- a/adapters/somoaudience/usersync.go +++ b/adapters/somoaudience/usersync.go @@ -3,8 +3,8 @@ package somoaudience import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSomoaudienceSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/somoaudience/usersync_test.go b/adapters/somoaudience/usersync_test.go index 86460506d68..9abcc65e748 100644 --- a/adapters/somoaudience/usersync_test.go +++ b/adapters/somoaudience/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sonobi/params_test.go b/adapters/sonobi/params_test.go index 3e9d63e8101..00fe63c6b6e 100644 --- a/adapters/sonobi/params_test.go +++ b/adapters/sonobi/params_test.go @@ -2,7 +2,7 @@ package sonobi import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/sonobi/sonobi.go b/adapters/sonobi/sonobi.go index a13e9c67d9a..3deebbbd9d4 100644 --- a/adapters/sonobi/sonobi.go +++ b/adapters/sonobi/sonobi.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // SonobiAdapter - Sonobi SonobiAdapter definition diff --git a/adapters/sonobi/sonobi_test.go b/adapters/sonobi/sonobi_test.go index 7e8f2dc2916..7a5d94bd5ac 100644 --- a/adapters/sonobi/sonobi_test.go +++ b/adapters/sonobi/sonobi_test.go @@ -3,9 +3,9 @@ package sonobi import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/sonobi/usersync.go b/adapters/sonobi/usersync.go index 6986d0fd8a1..6ac950563bc 100644 --- a/adapters/sonobi/usersync.go +++ b/adapters/sonobi/usersync.go @@ -1,8 +1,8 @@ package sonobi import ( - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "text/template" ) diff --git a/adapters/sonobi/usersync_test.go b/adapters/sonobi/usersync_test.go index bfe5859e5d1..8eadaef8f9c 100644 --- a/adapters/sonobi/usersync_test.go +++ b/adapters/sonobi/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/sovrn/sovrn.go b/adapters/sovrn/sovrn.go index 9e904872903..24920fa7998 100644 --- a/adapters/sovrn/sovrn.go +++ b/adapters/sovrn/sovrn.go @@ -12,12 +12,12 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" "golang.org/x/net/context/ctxhttp" ) diff --git a/adapters/sovrn/sovrn_test.go b/adapters/sovrn/sovrn_test.go index aa1b98f00f5..5f43c1a569f 100644 --- a/adapters/sovrn/sovrn_test.go +++ b/adapters/sovrn/sovrn_test.go @@ -8,10 +8,10 @@ import ( "net/http/httptest" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "context" "net/http" @@ -19,10 +19,10 @@ import ( "strconv" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/sovrn/usersync.go b/adapters/sovrn/usersync.go index b4de623718a..3f4e81439c6 100644 --- a/adapters/sovrn/usersync.go +++ b/adapters/sovrn/usersync.go @@ -3,8 +3,8 @@ package sovrn import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSovrnSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/sovrn/usersync_test.go b/adapters/sovrn/usersync_test.go index 4a4dfd9d1b9..e91c73245bb 100644 --- a/adapters/sovrn/usersync_test.go +++ b/adapters/sovrn/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/synacormedia/params_test.go b/adapters/synacormedia/params_test.go index a216818e382..d7585c9d58c 100644 --- a/adapters/synacormedia/params_test.go +++ b/adapters/synacormedia/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/synacormedia.json diff --git a/adapters/synacormedia/synacormedia.go b/adapters/synacormedia/synacormedia.go index 7c9b4fab19f..e87c665da59 100644 --- a/adapters/synacormedia/synacormedia.go +++ b/adapters/synacormedia/synacormedia.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type SynacorMediaAdapter struct { diff --git a/adapters/synacormedia/synacormedia_test.go b/adapters/synacormedia/synacormedia_test.go index 6a018dc3287..ce696a67ee1 100644 --- a/adapters/synacormedia/synacormedia_test.go +++ b/adapters/synacormedia/synacormedia_test.go @@ -3,9 +3,9 @@ package synacormedia import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/synacormedia/usersync.go b/adapters/synacormedia/usersync.go index 988c1b9ef6a..d7de9150744 100644 --- a/adapters/synacormedia/usersync.go +++ b/adapters/synacormedia/usersync.go @@ -3,8 +3,8 @@ package synacormedia import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewSynacorMediaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/synacormedia/usersync_test.go b/adapters/synacormedia/usersync_test.go index 538efb30c73..7ee7b1aa19b 100644 --- a/adapters/synacormedia/usersync_test.go +++ b/adapters/synacormedia/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/syncer.go b/adapters/syncer.go index 122bcc7ed38..13985db6275 100644 --- a/adapters/syncer.go +++ b/adapters/syncer.go @@ -3,10 +3,10 @@ package adapters import ( "text/template" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func GDPRAwareSyncerIDs(syncers map[openrtb_ext.BidderName]usersync.Usersyncer) map[openrtb_ext.BidderName]uint16 { diff --git a/adapters/syncer_test.go b/adapters/syncer_test.go index ca33a9a130d..7961608c29d 100644 --- a/adapters/syncer_test.go +++ b/adapters/syncer_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/tappx/params_test.go b/adapters/tappx/params_test.go index 3a73d4dab53..d6fcbb9cff6 100644 --- a/adapters/tappx/params_test.go +++ b/adapters/tappx/params_test.go @@ -2,7 +2,7 @@ package tappx import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/tappx/tappx.go b/adapters/tappx/tappx.go index de8cb49a471..2e044e82bb9 100644 --- a/adapters/tappx/tappx.go +++ b/adapters/tappx/tappx.go @@ -9,12 +9,12 @@ import ( "text/template" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const TAPPX_BIDDER_VERSION = "1.1" diff --git a/adapters/tappx/tappx_test.go b/adapters/tappx/tappx_test.go index 5346b82b694..465828b0bdf 100644 --- a/adapters/tappx/tappx_test.go +++ b/adapters/tappx/tappx_test.go @@ -4,9 +4,9 @@ import ( "regexp" "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/telaria/params_test.go b/adapters/telaria/params_test.go index efa3fba1be9..76f936cecfc 100644 --- a/adapters/telaria/params_test.go +++ b/adapters/telaria/params_test.go @@ -2,7 +2,7 @@ package telaria import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go index d99d4a98616..57cb53929b8 100644 --- a/adapters/telaria/telaria.go +++ b/adapters/telaria/telaria.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const Endpoint = "https://ads.tremorhub.com/ad/rtb/prebid" diff --git a/adapters/telaria/telaria_test.go b/adapters/telaria/telaria_test.go index be29ed40f03..b7eddbad523 100644 --- a/adapters/telaria/telaria_test.go +++ b/adapters/telaria/telaria_test.go @@ -3,9 +3,9 @@ package telaria import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/telaria/usersync.go b/adapters/telaria/usersync.go index e3f76f6e9b4..71cf85b6110 100644 --- a/adapters/telaria/usersync.go +++ b/adapters/telaria/usersync.go @@ -3,8 +3,8 @@ package telaria import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewTelariaSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/telaria/usersync_test.go b/adapters/telaria/usersync_test.go index 4896b253d2f..dc3accbbafa 100644 --- a/adapters/telaria/usersync_test.go +++ b/adapters/telaria/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/triplelift/triplelift.go b/adapters/triplelift/triplelift.go index 0b100094dbb..6b9f5ecfd41 100644 --- a/adapters/triplelift/triplelift.go +++ b/adapters/triplelift/triplelift.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type TripleliftAdapter struct { diff --git a/adapters/triplelift/triplelift_test.go b/adapters/triplelift/triplelift_test.go index 5107d7cc997..3f23e0dcdc6 100644 --- a/adapters/triplelift/triplelift_test.go +++ b/adapters/triplelift/triplelift_test.go @@ -3,9 +3,9 @@ package triplelift import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/triplelift/usersync.go b/adapters/triplelift/usersync.go index 5cb524bea11..0bd678b0e14 100644 --- a/adapters/triplelift/usersync.go +++ b/adapters/triplelift/usersync.go @@ -3,8 +3,8 @@ package triplelift import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/triplelift/usersync_test.go b/adapters/triplelift/usersync_test.go index 1731b22dffb..30b1a33b3e9 100644 --- a/adapters/triplelift/usersync_test.go +++ b/adapters/triplelift/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/triplelift_native/triplelift_native.go b/adapters/triplelift_native/triplelift_native.go index 9936bf82283..65c6994c0b3 100644 --- a/adapters/triplelift_native/triplelift_native.go +++ b/adapters/triplelift_native/triplelift_native.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type TripleliftNativeAdapter struct { diff --git a/adapters/triplelift_native/triplelift_native_test.go b/adapters/triplelift_native/triplelift_native_test.go index fef24949e79..19b40232155 100644 --- a/adapters/triplelift_native/triplelift_native_test.go +++ b/adapters/triplelift_native/triplelift_native_test.go @@ -3,9 +3,9 @@ package triplelift_native import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/triplelift_native/usersync.go b/adapters/triplelift_native/usersync.go index b7587b77950..a8d246f07cd 100644 --- a/adapters/triplelift_native/usersync.go +++ b/adapters/triplelift_native/usersync.go @@ -3,8 +3,8 @@ package triplelift_native import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/triplelift_native/usersync_test.go b/adapters/triplelift_native/usersync_test.go index df0757edc9f..ec229e2e68c 100644 --- a/adapters/triplelift_native/usersync_test.go +++ b/adapters/triplelift_native/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/ucfunnel/params_test.go b/adapters/ucfunnel/params_test.go index 4faec8739da..c33bc89a6b6 100644 --- a/adapters/ucfunnel/params_test.go +++ b/adapters/ucfunnel/params_test.go @@ -2,7 +2,7 @@ package ucfunnel import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/ucfunnel/ucfunnel.go b/adapters/ucfunnel/ucfunnel.go index 8df715e38be..7a6060f2930 100644 --- a/adapters/ucfunnel/ucfunnel.go +++ b/adapters/ucfunnel/ucfunnel.go @@ -6,11 +6,11 @@ import ( "net/http" "net/url" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type UcfunnelAdapter struct { diff --git a/adapters/ucfunnel/ucfunnel_test.go b/adapters/ucfunnel/ucfunnel_test.go index 216c06dff7f..3860c988b32 100644 --- a/adapters/ucfunnel/ucfunnel_test.go +++ b/adapters/ucfunnel/ucfunnel_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestMakeRequests(t *testing.T) { diff --git a/adapters/ucfunnel/usersync.go b/adapters/ucfunnel/usersync.go index 92eba0d73e0..8220fe6e1fa 100644 --- a/adapters/ucfunnel/usersync.go +++ b/adapters/ucfunnel/usersync.go @@ -3,8 +3,8 @@ package ucfunnel import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewUcfunnelSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/ucfunnel/usersync_test.go b/adapters/ucfunnel/usersync_test.go index 45320b8cac1..25d879d808a 100644 --- a/adapters/ucfunnel/usersync_test.go +++ b/adapters/ucfunnel/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/unruly/params_test.go b/adapters/unruly/params_test.go index 9b8f3110912..af1f2f2bce8 100644 --- a/adapters/unruly/params_test.go +++ b/adapters/unruly/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/unruly/unruly.go b/adapters/unruly/unruly.go index ae0243aaf4e..131e53af393 100644 --- a/adapters/unruly/unruly.go +++ b/adapters/unruly/unruly.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type UnrulyAdapter struct { diff --git a/adapters/unruly/unruly_test.go b/adapters/unruly/unruly_test.go index 18a21b7dfd6..e0f3ccc75e0 100644 --- a/adapters/unruly/unruly_test.go +++ b/adapters/unruly/unruly_test.go @@ -7,12 +7,12 @@ import ( "reflect" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/unruly/usersync.go b/adapters/unruly/usersync.go index c90052a475f..248b4923875 100644 --- a/adapters/unruly/usersync.go +++ b/adapters/unruly/usersync.go @@ -3,8 +3,8 @@ package unruly import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewUnrulySyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/unruly/usersync_test.go b/adapters/unruly/usersync_test.go index 2f0e07d813a..e85a066dddc 100644 --- a/adapters/unruly/usersync_test.go +++ b/adapters/unruly/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/valueimpression/params_test.go b/adapters/valueimpression/params_test.go index 46471de24bb..b80962ff4dd 100644 --- a/adapters/valueimpression/params_test.go +++ b/adapters/valueimpression/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/valueimpression.json diff --git a/adapters/valueimpression/usersync.go b/adapters/valueimpression/usersync.go index 34addbc0e75..4cc1539103b 100644 --- a/adapters/valueimpression/usersync.go +++ b/adapters/valueimpression/usersync.go @@ -3,8 +3,8 @@ package valueimpression import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewValueImpressionSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/valueimpression/usersync_test.go b/adapters/valueimpression/usersync_test.go index ffb3f372bd7..ed75969ac0d 100644 --- a/adapters/valueimpression/usersync_test.go +++ b/adapters/valueimpression/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/valueimpression/valueimpression.go b/adapters/valueimpression/valueimpression.go index 5cbcf3e19b5..87b66674f85 100644 --- a/adapters/valueimpression/valueimpression.go +++ b/adapters/valueimpression/valueimpression.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ValueImpressionAdapter struct { diff --git a/adapters/valueimpression/valueimpression_test.go b/adapters/valueimpression/valueimpression_test.go index f4d33864978..b2c6500389f 100644 --- a/adapters/valueimpression/valueimpression_test.go +++ b/adapters/valueimpression/valueimpression_test.go @@ -3,9 +3,9 @@ package valueimpression import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/verizonmedia/params_test.go b/adapters/verizonmedia/params_test.go index febda6058e6..9250c265526 100644 --- a/adapters/verizonmedia/params_test.go +++ b/adapters/verizonmedia/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/verizonmedia.json diff --git a/adapters/verizonmedia/usersync.go b/adapters/verizonmedia/usersync.go index 31fb12a2064..612aab3b1f0 100644 --- a/adapters/verizonmedia/usersync.go +++ b/adapters/verizonmedia/usersync.go @@ -1,8 +1,8 @@ package verizonmedia import ( - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "text/template" ) diff --git a/adapters/verizonmedia/usersync_test.go b/adapters/verizonmedia/usersync_test.go index ad77ef0e6cb..6455078a6f5 100644 --- a/adapters/verizonmedia/usersync_test.go +++ b/adapters/verizonmedia/usersync_test.go @@ -4,7 +4,7 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" "github.com/stretchr/testify/assert" ) diff --git a/adapters/verizonmedia/verizonmedia.go b/adapters/verizonmedia/verizonmedia.go index a721b67cf05..d90deea324b 100644 --- a/adapters/verizonmedia/verizonmedia.go +++ b/adapters/verizonmedia/verizonmedia.go @@ -6,11 +6,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type VerizonMediaAdapter struct { diff --git a/adapters/verizonmedia/verizonmedia_test.go b/adapters/verizonmedia/verizonmedia_test.go index 869ae9e9faa..e0e46c462c1 100644 --- a/adapters/verizonmedia/verizonmedia_test.go +++ b/adapters/verizonmedia/verizonmedia_test.go @@ -3,10 +3,10 @@ package verizonmedia import ( "testing" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/influxdata/influxdb/pkg/testing/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" ) func TestVerizonMediaBidderEndpointConfig(t *testing.T) { diff --git a/adapters/visx/params_test.go b/adapters/visx/params_test.go index e857e8d2639..f59ce49a46d 100644 --- a/adapters/visx/params_test.go +++ b/adapters/visx/params_test.go @@ -2,7 +2,7 @@ package visx import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/visx/usersync.go b/adapters/visx/usersync.go index fe1f5a42a10..e8223083033 100644 --- a/adapters/visx/usersync.go +++ b/adapters/visx/usersync.go @@ -3,8 +3,8 @@ package visx import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewVisxSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/visx/usersync_test.go b/adapters/visx/usersync_test.go index b410cda6061..e71ee35f5d8 100644 --- a/adapters/visx/usersync_test.go +++ b/adapters/visx/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/visx/visx.go b/adapters/visx/visx.go index f5a21f2269e..2a00b2def69 100644 --- a/adapters/visx/visx.go +++ b/adapters/visx/visx.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type VisxAdapter struct { diff --git a/adapters/visx/visx_test.go b/adapters/visx/visx_test.go index 2f51af76597..d637e908913 100644 --- a/adapters/visx/visx_test.go +++ b/adapters/visx/visx_test.go @@ -3,9 +3,9 @@ package visx import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/vrtcal/params_test.go b/adapters/vrtcal/params_test.go index ba57b6d82f8..5f80812f26f 100644 --- a/adapters/vrtcal/params_test.go +++ b/adapters/vrtcal/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) //Vrtcal doesn't currently require any custom fields. This file is included for conformity only diff --git a/adapters/vrtcal/usersync.go b/adapters/vrtcal/usersync.go index 04e52955033..0fd97875a0e 100644 --- a/adapters/vrtcal/usersync.go +++ b/adapters/vrtcal/usersync.go @@ -3,8 +3,8 @@ package vrtcal import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewVrtcalSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/vrtcal/usersync_test.go b/adapters/vrtcal/usersync_test.go index 26e5838dbe3..faea653f795 100644 --- a/adapters/vrtcal/usersync_test.go +++ b/adapters/vrtcal/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/vrtcal/vrtcal.go b/adapters/vrtcal/vrtcal.go index dadb03238ae..5d4bd705d2f 100644 --- a/adapters/vrtcal/vrtcal.go +++ b/adapters/vrtcal/vrtcal.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type VrtcalAdapter struct { diff --git a/adapters/vrtcal/vrtcal_test.go b/adapters/vrtcal/vrtcal_test.go index 332217650f6..c1f3cfb0796 100644 --- a/adapters/vrtcal/vrtcal_test.go +++ b/adapters/vrtcal/vrtcal_test.go @@ -3,9 +3,9 @@ package vrtcal import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/yeahmobi/params_test.go b/adapters/yeahmobi/params_test.go index 997bf93a53f..79b8273a362 100644 --- a/adapters/yeahmobi/params_test.go +++ b/adapters/yeahmobi/params_test.go @@ -2,7 +2,7 @@ package yeahmobi import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/yeahmobi/yeahmobi.go b/adapters/yeahmobi/yeahmobi.go index c62f5bc032f..0e342052008 100644 --- a/adapters/yeahmobi/yeahmobi.go +++ b/adapters/yeahmobi/yeahmobi.go @@ -7,13 +7,13 @@ import ( "net/url" "text/template" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" ) type YeahmobiAdapter struct { diff --git a/adapters/yeahmobi/yeahmobi_test.go b/adapters/yeahmobi/yeahmobi_test.go index a38480b0486..9f983633452 100644 --- a/adapters/yeahmobi/yeahmobi_test.go +++ b/adapters/yeahmobi/yeahmobi_test.go @@ -3,9 +3,9 @@ package yeahmobi import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go index 8c230c15b15..f66121e35e8 100644 --- a/adapters/yieldlab/params_test.go +++ b/adapters/yieldlab/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/yieldlab.json diff --git a/adapters/yieldlab/usersync.go b/adapters/yieldlab/usersync.go index 3ee9a3fdfb5..a0462e19e6e 100644 --- a/adapters/yieldlab/usersync.go +++ b/adapters/yieldlab/usersync.go @@ -3,8 +3,8 @@ package yieldlab import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewYieldlabSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/yieldlab/usersync_test.go b/adapters/yieldlab/usersync_test.go index 3892c16bf05..cdca7f9f417 100644 --- a/adapters/yieldlab/usersync_test.go +++ b/adapters/yieldlab/usersync_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ) func TestYieldlabSyncer(t *testing.T) { diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go index 086ce059898..adce598d60d 100644 --- a/adapters/yieldlab/yieldlab.go +++ b/adapters/yieldlab/yieldlab.go @@ -9,13 +9,13 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "golang.org/x/text/currency" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // YieldlabAdapter connects the Yieldlab API to prebid server diff --git a/adapters/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go index 273117e3bdf..83f7d1e813f 100644 --- a/adapters/yieldlab/yieldlab_test.go +++ b/adapters/yieldlab/yieldlab_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const testURL = "https://ad.yieldlab.net/testing/" diff --git a/adapters/yieldmo/params_test.go b/adapters/yieldmo/params_test.go index 0a8fe2d10f1..87044a2dd57 100644 --- a/adapters/yieldmo/params_test.go +++ b/adapters/yieldmo/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // This file actually intends to test static/bidder-params/yieldmo.json diff --git a/adapters/yieldmo/usersync.go b/adapters/yieldmo/usersync.go index 041e7e8f073..25d65f229a2 100644 --- a/adapters/yieldmo/usersync.go +++ b/adapters/yieldmo/usersync.go @@ -3,8 +3,8 @@ package yieldmo import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewYieldmoSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/yieldmo/usersync_test.go b/adapters/yieldmo/usersync_test.go index 598710ec742..1212efdb878 100644 --- a/adapters/yieldmo/usersync_test.go +++ b/adapters/yieldmo/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/yieldmo/yieldmo.go b/adapters/yieldmo/yieldmo.go index 2d84444441e..95fbfa33f48 100644 --- a/adapters/yieldmo/yieldmo.go +++ b/adapters/yieldmo/yieldmo.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type YieldmoAdapter struct { diff --git a/adapters/yieldmo/yieldmo_test.go b/adapters/yieldmo/yieldmo_test.go index cb0a8d60aa5..faee55c3890 100644 --- a/adapters/yieldmo/yieldmo_test.go +++ b/adapters/yieldmo/yieldmo_test.go @@ -3,9 +3,9 @@ package yieldmo import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/yieldone/params_test.go b/adapters/yieldone/params_test.go index 6048ea5d7dc..e0142334d6e 100644 --- a/adapters/yieldone/params_test.go +++ b/adapters/yieldone/params_test.go @@ -2,7 +2,7 @@ package yieldone import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "testing" ) diff --git a/adapters/yieldone/usersync.go b/adapters/yieldone/usersync.go index bc9d1b3235b..333550aa775 100644 --- a/adapters/yieldone/usersync.go +++ b/adapters/yieldone/usersync.go @@ -3,8 +3,8 @@ package yieldone import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewYieldoneSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/yieldone/usersync_test.go b/adapters/yieldone/usersync_test.go index 902f3b66b34..730f9103017 100644 --- a/adapters/yieldone/usersync_test.go +++ b/adapters/yieldone/usersync_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/yieldone/yieldone.go b/adapters/yieldone/yieldone.go index 5d7edd63018..33debb26bd9 100644 --- a/adapters/yieldone/yieldone.go +++ b/adapters/yieldone/yieldone.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type YieldoneAdapter struct { diff --git a/adapters/yieldone/yieldone_test.go b/adapters/yieldone/yieldone_test.go index 8544c21f4e7..ca2c3851ad4 100644 --- a/adapters/yieldone/yieldone_test.go +++ b/adapters/yieldone/yieldone_test.go @@ -3,9 +3,9 @@ package yieldone import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/zeroclickfraud/usersync.go b/adapters/zeroclickfraud/usersync.go index 833524e4b3e..a5435335ab8 100644 --- a/adapters/zeroclickfraud/usersync.go +++ b/adapters/zeroclickfraud/usersync.go @@ -3,8 +3,8 @@ package zeroclickfraud import ( "text/template" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) func NewZeroClickFraudSyncer(temp *template.Template) usersync.Usersyncer { diff --git a/adapters/zeroclickfraud/usersync_test.go b/adapters/zeroclickfraud/usersync_test.go index 5e8f8fdf111..445b07ab8eb 100644 --- a/adapters/zeroclickfraud/usersync_test.go +++ b/adapters/zeroclickfraud/usersync_test.go @@ -4,9 +4,9 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/adapters/zeroclickfraud/zeroclickfraud.go b/adapters/zeroclickfraud/zeroclickfraud.go index 17fbba747df..eb98aed1ece 100644 --- a/adapters/zeroclickfraud/zeroclickfraud.go +++ b/adapters/zeroclickfraud/zeroclickfraud.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/macros" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) type ZeroClickFraudAdapter struct { diff --git a/adapters/zeroclickfraud/zeroclickfraud_test.go b/adapters/zeroclickfraud/zeroclickfraud_test.go index 654d9450da0..2942c25a4f3 100644 --- a/adapters/zeroclickfraud/zeroclickfraud_test.go +++ b/adapters/zeroclickfraud/zeroclickfraud_test.go @@ -3,9 +3,9 @@ package zeroclickfraud import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/amp/parse.go b/amp/parse.go index 9e0e019f953..c3606e83563 100644 --- a/amp/parse.go +++ b/amp/parse.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // Params defines the paramters of an AMP request. diff --git a/amp/parse_test.go b/amp/parse_test.go index e2c82d71030..1b9d30804da 100644 --- a/amp/parse_test.go +++ b/amp/parse_test.go @@ -4,7 +4,7 @@ import ( "net/http" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/analytics/clients/http.go b/analytics/clients/http.go index bc7f863ebdd..68af3073a78 100644 --- a/analytics/clients/http.go +++ b/analytics/clients/http.go @@ -7,6 +7,6 @@ import ( var defaultHttpInstance = http.DefaultClient func GetDefaultHttpInstance() *http.Client { - // TODO 2020-06-22 @see https://github.com/prebid/prebid-server/pull/1331#discussion_r436110097 + // TODO 2020-06-22 @see https://github.com/PubMatic-OpenWrap/prebid-server/pull/1331#discussion_r436110097 return defaultHttpInstance } diff --git a/analytics/config/config.go b/analytics/config/config.go index fa63cb5a1e4..ac145ee363e 100644 --- a/analytics/config/config.go +++ b/analytics/config/config.go @@ -1,12 +1,12 @@ package config import ( + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/analytics/clients" + "github.com/PubMatic-OpenWrap/prebid-server/analytics/filesystem" + "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/clients" - "github.com/prebid/prebid-server/analytics/filesystem" - "github.com/prebid/prebid-server/analytics/pubstack" - "github.com/prebid/prebid-server/config" ) //Modules that need to be logged to need to be initialized here diff --git a/analytics/config/config_test.go b/analytics/config/config_test.go index 52edc79c2e3..02ea6a54261 100644 --- a/analytics/config/config_test.go +++ b/analytics/config/config_test.go @@ -6,9 +6,9 @@ import ( "os" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" ) const TEST_DIR string = "testFiles" diff --git a/analytics/core.go b/analytics/core.go index 6837304541d..78ca1df2c9a 100644 --- a/analytics/core.go +++ b/analytics/core.go @@ -3,10 +3,10 @@ package analytics import ( "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) /* diff --git a/analytics/filesystem/file_module.go b/analytics/filesystem/file_module.go index a0721d98a2a..bb94fa783a5 100644 --- a/analytics/filesystem/file_module.go +++ b/analytics/filesystem/file_module.go @@ -5,8 +5,8 @@ import ( "encoding/json" "fmt" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" "github.com/chasex/glog" - "github.com/prebid/prebid-server/analytics" ) type RequestType string diff --git a/analytics/filesystem/file_module_test.go b/analytics/filesystem/file_module_test.go index cfc923c70f6..e46e9259319 100644 --- a/analytics/filesystem/file_module_test.go +++ b/analytics/filesystem/file_module_test.go @@ -1,15 +1,15 @@ package filesystem import ( - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" "net/http" "os" "strings" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" ) const TEST_DIR string = "testFiles" diff --git a/analytics/pubstack/helpers/json.go b/analytics/pubstack/helpers/json.go index f02f1120626..11c5bf8ec05 100644 --- a/analytics/pubstack/helpers/json.go +++ b/analytics/pubstack/helpers/json.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/prebid/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" ) func JsonifyAuctionObject(ao *analytics.AuctionObject, scope string) ([]byte, error) { diff --git a/analytics/pubstack/helpers/json_test.go b/analytics/pubstack/helpers/json_test.go index 4e36e8db2be..4f453ccdf58 100644 --- a/analytics/pubstack/helpers/json_test.go +++ b/analytics/pubstack/helpers/json_test.go @@ -1,9 +1,9 @@ package helpers import ( - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "net/http" "testing" ) diff --git a/analytics/pubstack/pubstack_module.go b/analytics/pubstack/pubstack_module.go index 60ccde02a8c..ae32dc52638 100644 --- a/analytics/pubstack/pubstack_module.go +++ b/analytics/pubstack/pubstack_module.go @@ -2,7 +2,7 @@ package pubstack import ( "fmt" - "github.com/prebid/prebid-server/analytics/pubstack/eventchannel" + "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack/eventchannel" "net/http" "net/url" "os" @@ -11,10 +11,10 @@ import ( "syscall" "time" + "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack/helpers" "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics/pubstack/helpers" - "github.com/prebid/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" ) type Configuration struct { diff --git a/analytics/pubstack/pubstack_module_test.go b/analytics/pubstack/pubstack_module_test.go index 6bebc937f5e..143fb217913 100644 --- a/analytics/pubstack/pubstack_module_test.go +++ b/analytics/pubstack/pubstack_module_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" "github.com/stretchr/testify/assert" ) diff --git a/cache/dummycache/dummycache.go b/cache/dummycache/dummycache.go index 8cf9f2c4dff..245b36325aa 100644 --- a/cache/dummycache/dummycache.go +++ b/cache/dummycache/dummycache.go @@ -3,7 +3,7 @@ package dummycache import ( "fmt" - "github.com/prebid/prebid-server/cache" + "github.com/PubMatic-OpenWrap/prebid-server/cache" ) // Cache dummy config that will echo back results diff --git a/cache/filecache/filecache.go b/cache/filecache/filecache.go index 0c1ba435388..d49f755edb4 100644 --- a/cache/filecache/filecache.go +++ b/cache/filecache/filecache.go @@ -4,8 +4,8 @@ import ( "fmt" "io/ioutil" + "github.com/PubMatic-OpenWrap/prebid-server/cache" "github.com/golang/glog" - "github.com/prebid/prebid-server/cache" "gopkg.in/yaml.v2" ) diff --git a/cache/postgrescache/postgrescache.go b/cache/postgrescache/postgrescache.go index df8b8fe49b2..aa0d3c3ea8b 100644 --- a/cache/postgrescache/postgrescache.go +++ b/cache/postgrescache/postgrescache.go @@ -7,11 +7,11 @@ import ( "encoding/gob" "time" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/cache" "github.com/coocood/freecache" "github.com/lib/pq" - "github.com/prebid/prebid-server/cache" ) type CacheConfig struct { diff --git a/config/adapter.go b/config/adapter.go index ff262b186fd..5f188ac4a62 100644 --- a/config/adapter.go +++ b/config/adapter.go @@ -4,8 +4,8 @@ import ( "fmt" "text/template" + "github.com/PubMatic-OpenWrap/prebid-server/macros" validator "github.com/asaskevich/govalidator" - "github.com/prebid/prebid-server/macros" ) type Adapter struct { diff --git a/config/config.go b/config/config.go index ae1f62e90b4..11a0e94982c 100755 --- a/config/config.go +++ b/config/config.go @@ -9,9 +9,9 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" "github.com/spf13/viper" ) @@ -790,7 +790,7 @@ func SetupViper(v *viper.Viper, filename string) { } // Disabling adapters by default that require some specific config params. - // If you're using one of these, make sure you check out the documentation (https://github.com/prebid/prebid-server/tree/master/docs/bidders) + // If you're using one of these, make sure you check out the documentation (https://github.com/PubMatic-OpenWrap/prebid-server/tree/master/docs/bidders) // for them and specify all the parameters they need for them to work correctly. v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb") v.SetDefault("adapters.33across.partner_id", "") diff --git a/config/config_test.go b/config/config_test.go index 7ff5f0fafa1..f6792cb7648 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -11,7 +11,7 @@ import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/spf13/viper" "github.com/stretchr/testify/assert" ) diff --git a/currency/rate_converter.go b/currency/rate_converter.go index 39b9cd59ca2..269cf7551ea 100644 --- a/currency/rate_converter.go +++ b/currency/rate_converter.go @@ -8,9 +8,9 @@ import ( "sync/atomic" "time" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/util/timeutil" "github.com/golang/glog" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/util/timeutil" ) // RateConverter holds the currencies conversion rates dictionary diff --git a/currency/rate_converter_test.go b/currency/rate_converter_test.go index e1d6741dff2..b3e26c86a47 100644 --- a/currency/rate_converter_test.go +++ b/currency/rate_converter_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/util/task" + "github.com/PubMatic-OpenWrap/prebid-server/util/task" "github.com/stretchr/testify/assert" ) diff --git a/diff b/diff new file mode 100644 index 00000000000..ede8cbc6f26 --- /dev/null +++ b/diff @@ -0,0 +1,10811 @@ +diff --git a/.travis.yml b/.travis.yml +index 97d8cea4..655ea837 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -4,7 +4,7 @@ go: + - '1.14.2' + - '1.15' + +-go_import_path: github.com/prebid/prebid-server ++go_import_path: github.com/PubMatic-OpenWrap/prebid-server + + env: + - GO111MODULE=on +diff --git a/Makefile b/Makefile +index 8ffea91f..8475ce83 100644 +--- a/Makefile ++++ b/Makefile +@@ -15,7 +15,7 @@ test: deps + ifeq "$(adapter)" "" + ./validate.sh + else +- go test github.com/prebid/prebid-server/adapters/$(adapter) -bench=. ++ go test github.com/PubMatic-OpenWrap/prebid-server/adapters/$(adapter) -bench=. + endif + + # build will ensure all of our tests pass and then build the go binary +diff --git a/README.md b/README.md +index 32bf4575..529629e0 100644 +--- a/README.md ++++ b/README.md +@@ -1,5 +1,5 @@ + [![Build Status](https://travis-ci.org/prebid/prebid-server.svg?branch=master)](https://travis-ci.org/prebid/prebid-server) +-[![Go Report Card](https://goreportcard.com/badge/github.com/prebid/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/prebid/prebid-server) ++[![Go Report Card](https://goreportcard.com/badge/github.com/PubMatic-OpenWrap/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/PubMatic-OpenWrap/prebid-server) + + # Prebid Server + +@@ -27,8 +27,8 @@ Download and prepare Prebid Server: + + ```bash + cd YOUR_DIRECTORY +-git clone https://github.com/prebid/prebid-server src/github.com/prebid/prebid-server +-cd src/github.com/prebid/prebid-server ++git clone https://github.com/PubMatic-OpenWrap/prebid-server src/github.com/PubMatic-OpenWrap/prebid-server ++cd src/github.com/PubMatic-OpenWrap/prebid-server + ``` + + Run the automated tests: +@@ -52,9 +52,9 @@ For the full API reference, see [the endpoint documentation](https://docs.prebid + + Want to [add an adapter](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html)? Found a bug? Great! + +-Report bugs, request features, and suggest improvements [on Github](https://github.com/prebid/prebid-server/issues). ++Report bugs, request features, and suggest improvements [on Github](https://github.com/PubMatic-OpenWrap/prebid-server/issues). + +-Or better yet, [open a pull request](https://github.com/prebid/prebid-server/compare) with the changes you'd like to see. ++Or better yet, [open a pull request](https://github.com/PubMatic-OpenWrap/prebid-server/compare) with the changes you'd like to see. + + ## IDE Setup for PBS-Go development + +diff --git a/account/account.go b/account/account.go +index 25a504ca..b16d1001 100644 +--- a/account/account.go ++++ b/account/account.go +@@ -6,10 +6,10 @@ import ( + "fmt" + + jsonpatch "github.com/evanphx/json-patch" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + // GetAccount looks up the config.Account object referenced by the given accountID, with access rules applied +diff --git a/account/account_test.go b/account/account_test.go +index 75c48a02..0783ee0e 100644 +--- a/account/account_test.go ++++ b/account/account_test.go +@@ -6,10 +6,10 @@ import ( + "fmt" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/33across/33across.go b/adapters/33across/33across.go +index b9655faa..7abd7ba7 100644 +--- a/adapters/33across/33across.go ++++ b/adapters/33across/33across.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type TtxAdapter struct { +diff --git a/adapters/33across/33across_test.go b/adapters/33across/33across_test.go +index 97703735..08c2ff61 100644 +--- a/adapters/33across/33across_test.go ++++ b/adapters/33across/33across_test.go +@@ -3,9 +3,9 @@ package ttx + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/33across/params_test.go b/adapters/33across/params_test.go +index 0c7cde18..19dfb221 100644 +--- a/adapters/33across/params_test.go ++++ b/adapters/33across/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/33across.json +diff --git a/adapters/33across/usersync.go b/adapters/33across/usersync.go +index 7bc9ae45..0bed70ee 100644 +--- a/adapters/33across/usersync.go ++++ b/adapters/33across/usersync.go +@@ -3,8 +3,8 @@ package ttx + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func New33AcrossSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/33across/usersync_test.go b/adapters/33across/usersync_test.go +index a9eb4e57..89cae0f3 100644 +--- a/adapters/33across/usersync_test.go ++++ b/adapters/33across/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/acuityads/acuityads.go b/adapters/acuityads/acuityads.go +index 9c6f73c2..ac2c6ac7 100644 +--- a/adapters/acuityads/acuityads.go ++++ b/adapters/acuityads/acuityads.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AcuityAdsAdapter struct { +diff --git a/adapters/acuityads/acuityads_test.go b/adapters/acuityads/acuityads_test.go +index 8cc50637..be12780a 100644 +--- a/adapters/acuityads/acuityads_test.go ++++ b/adapters/acuityads/acuityads_test.go +@@ -3,9 +3,9 @@ package acuityads + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/acuityads/params_test.go b/adapters/acuityads/params_test.go +index 892fe9a6..e1a47669 100644 +--- a/adapters/acuityads/params_test.go ++++ b/adapters/acuityads/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + var validParams = []string{ +diff --git a/adapters/acuityads/usersync.go b/adapters/acuityads/usersync.go +index 7eedf78d..90b610bb 100644 +--- a/adapters/acuityads/usersync.go ++++ b/adapters/acuityads/usersync.go +@@ -3,8 +3,8 @@ package acuityads + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAcuityAdsSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/acuityads/usersync_test.go b/adapters/acuityads/usersync_test.go +index 1f57cea3..5c6f6a43 100644 +--- a/adapters/acuityads/usersync_test.go ++++ b/adapters/acuityads/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adapterstest/adapter_test_util.go b/adapters/adapterstest/adapter_test_util.go +index a5b3bb8b..269eed08 100644 +--- a/adapters/adapterstest/adapter_test_util.go ++++ b/adapters/adapterstest/adapter_test_util.go +@@ -8,7 +8,7 @@ import ( + + "net/http" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // OrtbMockService Represents a scaffolded OpenRTB service. +diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go +index a25a4f19..b8c33064 100644 +--- a/adapters/adapterstest/test_json.go ++++ b/adapters/adapterstest/test_json.go +@@ -7,8 +7,8 @@ import ( + "regexp" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/yudai/gojsondiff" + "github.com/yudai/gojsondiff/formatter" + +diff --git a/adapters/adform/adform.go b/adapters/adform/adform.go +index e84df5b8..63253156 100644 +--- a/adapters/adform/adform.go ++++ b/adapters/adform/adform.go +@@ -13,14 +13,14 @@ import ( + "strconv" + "strings" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go +index 558453e4..14663c32 100644 +--- a/adapters/adform/adform_test.go ++++ b/adapters/adform/adform_test.go +@@ -10,17 +10,17 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "github.com/stretchr/testify/assert" + ) +diff --git a/adapters/adform/params_test.go b/adapters/adform/params_test.go +index b392463f..c4ffb793 100644 +--- a/adapters/adform/params_test.go ++++ b/adapters/adform/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/adform.json +diff --git a/adapters/adform/usersync.go b/adapters/adform/usersync.go +index 32b4e3a9..b98c1b6c 100644 +--- a/adapters/adform/usersync.go ++++ b/adapters/adform/usersync.go +@@ -3,8 +3,8 @@ package adform + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAdformSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/adform/usersync_test.go b/adapters/adform/usersync_test.go +index 855506da..3a507331 100644 +--- a/adapters/adform/usersync_test.go ++++ b/adapters/adform/usersync_test.go +@@ -6,8 +6,8 @@ import ( + + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + ) + + func TestAdformSyncer(t *testing.T) { +diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go +index b726d0c1..59a3ba5b 100644 +--- a/adapters/adgeneration/adgeneration.go ++++ b/adapters/adgeneration/adgeneration.go +@@ -10,11 +10,11 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdgenerationAdapter struct { +diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go +index f63c99cb..a4041a5a 100644 +--- a/adapters/adgeneration/adgeneration_test.go ++++ b/adapters/adgeneration/adgeneration_test.go +@@ -4,11 +4,11 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adgeneration/params_test.go b/adapters/adgeneration/params_test.go +index 062d122a..11a0dfe9 100644 +--- a/adapters/adgeneration/params_test.go ++++ b/adapters/adgeneration/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/adhese/adhese.go b/adapters/adhese/adhese.go +index e13c03a1..a84f872f 100644 +--- a/adapters/adhese/adhese.go ++++ b/adapters/adhese/adhese.go +@@ -11,12 +11,12 @@ import ( + "text/template" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdheseAdapter struct { +diff --git a/adapters/adhese/adhese_test.go b/adapters/adhese/adhese_test.go +index 3a9d28ee..40b28887 100644 +--- a/adapters/adhese/adhese_test.go ++++ b/adapters/adhese/adhese_test.go +@@ -3,9 +3,9 @@ package adhese + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adhese/params_test.go b/adapters/adhese/params_test.go +index 45024749..f9c3de62 100644 +--- a/adapters/adhese/params_test.go ++++ b/adapters/adhese/params_test.go +@@ -5,7 +5,7 @@ import ( + "fmt" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/adhese/utils.go b/adapters/adhese/utils.go +index 9983be96..7b09ad02 100644 +--- a/adapters/adhese/utils.go ++++ b/adapters/adhese/utils.go +@@ -1,6 +1,6 @@ + package adhese + +-import "github.com/mxmCherry/openrtb" ++import "github.com/PubMatic-OpenWrap/openrtb" + + type AdheseOriginData struct { + Priority string `json:"priority"` +diff --git a/adapters/adkernel/adkernel.go b/adapters/adkernel/adkernel.go +index 6bedeec6..f483ba7c 100644 +--- a/adapters/adkernel/adkernel.go ++++ b/adapters/adkernel/adkernel.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type adkernelAdapter struct { +diff --git a/adapters/adkernel/adkernel_test.go b/adapters/adkernel/adkernel_test.go +index e9fcaef1..a85769f5 100644 +--- a/adapters/adkernel/adkernel_test.go ++++ b/adapters/adkernel/adkernel_test.go +@@ -3,9 +3,9 @@ package adkernel + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adkernel/usersync.go b/adapters/adkernel/usersync.go +index 2e2ebdb9..7622e1da 100644 +--- a/adapters/adkernel/usersync.go ++++ b/adapters/adkernel/usersync.go +@@ -3,8 +3,8 @@ package adkernel + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const adkernelGDPRVendorID = uint16(14) +diff --git a/adapters/adkernel/usersync_test.go b/adapters/adkernel/usersync_test.go +index aeacf00b..7230fcba 100644 +--- a/adapters/adkernel/usersync_test.go ++++ b/adapters/adkernel/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adkernelAdn/adkernelAdn.go b/adapters/adkernelAdn/adkernelAdn.go +index acd27c9e..491bead4 100644 +--- a/adapters/adkernelAdn/adkernelAdn.go ++++ b/adapters/adkernelAdn/adkernelAdn.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const defaultDomain string = "tag.adkernel.com" +diff --git a/adapters/adkernelAdn/adkernelAdn_test.go b/adapters/adkernelAdn/adkernelAdn_test.go +index 687f5a94..a4311d3e 100644 +--- a/adapters/adkernelAdn/adkernelAdn_test.go ++++ b/adapters/adkernelAdn/adkernelAdn_test.go +@@ -3,9 +3,9 @@ package adkernelAdn + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adkernelAdn/usersync.go b/adapters/adkernelAdn/usersync.go +index 0bedac38..ab60edf2 100644 +--- a/adapters/adkernelAdn/usersync.go ++++ b/adapters/adkernelAdn/usersync.go +@@ -3,8 +3,8 @@ package adkernelAdn + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const adkernelGDPRVendorID = uint16(14) +diff --git a/adapters/adkernelAdn/usersync_test.go b/adapters/adkernelAdn/usersync_test.go +index 92d688e6..9528579a 100644 +--- a/adapters/adkernelAdn/usersync_test.go ++++ b/adapters/adkernelAdn/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adman/adman.go b/adapters/adman/adman.go +index e0f67db8..3c0342fe 100644 +--- a/adapters/adman/adman.go ++++ b/adapters/adman/adman.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // AdmanAdapter struct +diff --git a/adapters/adman/adman_test.go b/adapters/adman/adman_test.go +index ceee5f21..5dc10df8 100644 +--- a/adapters/adman/adman_test.go ++++ b/adapters/adman/adman_test.go +@@ -3,9 +3,9 @@ package adman + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/adman/params_test.go b/adapters/adman/params_test.go +index a80c2a44..4cea67cc 100644 +--- a/adapters/adman/params_test.go ++++ b/adapters/adman/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the adman schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/adman/usersync.go b/adapters/adman/usersync.go +index aae6afcd..f7edd8c5 100644 +--- a/adapters/adman/usersync.go ++++ b/adapters/adman/usersync.go +@@ -3,8 +3,8 @@ package adman + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewAdmanSyncer returns adman syncer +diff --git a/adapters/adman/usersync_test.go b/adapters/adman/usersync_test.go +index 25da77db..db67499e 100644 +--- a/adapters/adman/usersync_test.go ++++ b/adapters/adman/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/admixer/admixer.go b/adapters/admixer/admixer.go +index eff93746..b16dc007 100644 +--- a/adapters/admixer/admixer.go ++++ b/adapters/admixer/admixer.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdmixerAdapter struct { +diff --git a/adapters/admixer/admixer_test.go b/adapters/admixer/admixer_test.go +index d994847c..629d4df8 100644 +--- a/adapters/admixer/admixer_test.go ++++ b/adapters/admixer/admixer_test.go +@@ -3,9 +3,9 @@ package admixer + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/admixer/params_test.go b/adapters/admixer/params_test.go +index 71cccb6a..d25bdcec 100644 +--- a/adapters/admixer/params_test.go ++++ b/adapters/admixer/params_test.go +@@ -2,7 +2,7 @@ package admixer + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/admixer/usersync.go b/adapters/admixer/usersync.go +index 0a7f50ab..1df22cc2 100644 +--- a/adapters/admixer/usersync.go ++++ b/adapters/admixer/usersync.go +@@ -1,8 +1,8 @@ + package admixer + + import ( +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "text/template" + ) + +diff --git a/adapters/admixer/usersync_test.go b/adapters/admixer/usersync_test.go +index 79f023a2..8a43b866 100644 +--- a/adapters/admixer/usersync_test.go ++++ b/adapters/admixer/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adocean/adocean.go b/adapters/adocean/adocean.go +index 43aa067b..8310626f 100644 +--- a/adapters/adocean/adocean.go ++++ b/adapters/adocean/adocean.go +@@ -13,12 +13,12 @@ import ( + "strings" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const adapterVersion = "1.1.0" +diff --git a/adapters/adocean/adocean_test.go b/adapters/adocean/adocean_test.go +index 531204cd..b75de2a9 100644 +--- a/adapters/adocean/adocean_test.go ++++ b/adapters/adocean/adocean_test.go +@@ -3,9 +3,9 @@ package adocean + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adocean/params_test.go b/adapters/adocean/params_test.go +index 1a88c471..91e2fbdc 100644 +--- a/adapters/adocean/params_test.go ++++ b/adapters/adocean/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/adocean/usersync.go b/adapters/adocean/usersync.go +index 650e517a..4bfe39e1 100644 +--- a/adapters/adocean/usersync.go ++++ b/adapters/adocean/usersync.go +@@ -3,8 +3,8 @@ package adocean + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAdOceanSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/adocean/usersync_test.go b/adapters/adocean/usersync_test.go +index 9ca81b98..aa0bcb77 100644 +--- a/adapters/adocean/usersync_test.go ++++ b/adapters/adocean/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adoppler/adoppler.go b/adapters/adoppler/adoppler.go +index adbb177d..498bb4c7 100644 +--- a/adapters/adoppler/adoppler.go ++++ b/adapters/adoppler/adoppler.go +@@ -8,12 +8,12 @@ import ( + "net/url" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const DefaultClient = "app" +diff --git a/adapters/adoppler/adoppler_test.go b/adapters/adoppler/adoppler_test.go +index fb5cb22b..eab0ac57 100644 +--- a/adapters/adoppler/adoppler_test.go ++++ b/adapters/adoppler/adoppler_test.go +@@ -3,9 +3,9 @@ package adoppler + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/adot/adot.go b/adapters/adot/adot.go +index c58c4a1e..fcbb5a29 100644 +--- a/adapters/adot/adot.go ++++ b/adapters/adot/adot.go +@@ -3,11 +3,11 @@ package adot + import ( + "encoding/json" + "fmt" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "net/http" + ) + +diff --git a/adapters/adot/adot_test.go b/adapters/adot/adot_test.go +index fca6b303..2e6e7486 100644 +--- a/adapters/adot/adot_test.go ++++ b/adapters/adot/adot_test.go +@@ -2,11 +2,11 @@ package adot + + import ( + "encoding/json" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + "testing" + ) +diff --git a/adapters/adot/params_test.go b/adapters/adot/params_test.go +index 2f7b4b9a..2a6dc17d 100644 +--- a/adapters/adot/params_test.go ++++ b/adapters/adot/params_test.go +@@ -2,7 +2,7 @@ package adot + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/adpone/adpone.go b/adapters/adpone/adpone.go +index 7907ceb1..ec4cba75 100644 +--- a/adapters/adpone/adpone.go ++++ b/adapters/adpone/adpone.go +@@ -5,12 +5,12 @@ import ( + "fmt" + "net/http" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + ) + + // Builder builds a new instance of the Adpone adapter for the given bidder with the given config. +diff --git a/adapters/adpone/adpone_test.go b/adapters/adpone/adpone_test.go +index 78ad51ba..69726e31 100644 +--- a/adapters/adpone/adpone_test.go ++++ b/adapters/adpone/adpone_test.go +@@ -3,9 +3,9 @@ package adpone + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const testsDir = "adponetest" +diff --git a/adapters/adpone/usersync.go b/adapters/adpone/usersync.go +index 67d4c998..63f61609 100644 +--- a/adapters/adpone/usersync.go ++++ b/adapters/adpone/usersync.go +@@ -3,8 +3,8 @@ package adpone + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const adponeGDPRVendorID = uint16(799) +diff --git a/adapters/adpone/usersync_test.go b/adapters/adpone/usersync_test.go +index 87b4e9ae..7bc528b8 100644 +--- a/adapters/adpone/usersync_test.go ++++ b/adapters/adpone/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go +index 0db9218f..3f49b490 100644 +--- a/adapters/adprime/adprime.go ++++ b/adapters/adprime/adprime.go +@@ -6,11 +6,11 @@ import ( + "net/http" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // AdprimeAdapter struct +diff --git a/adapters/adprime/adprime_test.go b/adapters/adprime/adprime_test.go +index ff12381d..cfcf255a 100644 +--- a/adapters/adprime/adprime_test.go ++++ b/adapters/adprime/adprime_test.go +@@ -3,9 +3,9 @@ package adprime + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/adprime/params_test.go b/adapters/adprime/params_test.go +index 05adad5c..bea13e32 100644 +--- a/adapters/adprime/params_test.go ++++ b/adapters/adprime/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the adprime schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/adtarget/adtarget.go b/adapters/adtarget/adtarget.go +index 511918bb..070ede40 100644 +--- a/adapters/adtarget/adtarget.go ++++ b/adapters/adtarget/adtarget.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdtargetAdapter struct { +diff --git a/adapters/adtarget/adtarget_test.go b/adapters/adtarget/adtarget_test.go +index ed21aef0..bb20b40c 100644 +--- a/adapters/adtarget/adtarget_test.go ++++ b/adapters/adtarget/adtarget_test.go +@@ -3,9 +3,9 @@ package adtarget + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/adtarget/params_test.go b/adapters/adtarget/params_test.go +index b128d11c..61ed4885 100644 +--- a/adapters/adtarget/params_test.go ++++ b/adapters/adtarget/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/adtarget.json +diff --git a/adapters/adtarget/usersync.go b/adapters/adtarget/usersync.go +index 20bced25..93e57b17 100644 +--- a/adapters/adtarget/usersync.go ++++ b/adapters/adtarget/usersync.go +@@ -3,8 +3,8 @@ package adtarget + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAdtargetSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/adtarget/usersync_test.go b/adapters/adtarget/usersync_test.go +index ddba9e7a..419a6cb0 100644 +--- a/adapters/adtarget/usersync_test.go ++++ b/adapters/adtarget/usersync_test.go +@@ -5,10 +5,10 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/adtelligent/adtelligent.go b/adapters/adtelligent/adtelligent.go +index fd501fde..7d8f6099 100644 +--- a/adapters/adtelligent/adtelligent.go ++++ b/adapters/adtelligent/adtelligent.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdtelligentAdapter struct { +diff --git a/adapters/adtelligent/adtelligent_test.go b/adapters/adtelligent/adtelligent_test.go +index 503b30b5..6a104aa7 100644 +--- a/adapters/adtelligent/adtelligent_test.go ++++ b/adapters/adtelligent/adtelligent_test.go +@@ -3,9 +3,9 @@ package adtelligent + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/adtelligent/params_test.go b/adapters/adtelligent/params_test.go +index eb2aab0d..8d8eb6d1 100644 +--- a/adapters/adtelligent/params_test.go ++++ b/adapters/adtelligent/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/adtelligent.json +diff --git a/adapters/adtelligent/usersync.go b/adapters/adtelligent/usersync.go +index 087b5bdd..387c65bb 100644 +--- a/adapters/adtelligent/usersync.go ++++ b/adapters/adtelligent/usersync.go +@@ -3,8 +3,8 @@ package adtelligent + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAdtelligentSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/adtelligent/usersync_test.go b/adapters/adtelligent/usersync_test.go +index fa157d22..7cc92eb4 100644 +--- a/adapters/adtelligent/usersync_test.go ++++ b/adapters/adtelligent/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/advangelists/advangelists.go b/adapters/advangelists/advangelists.go +index 95ddec0f..249e3282 100644 +--- a/adapters/advangelists/advangelists.go ++++ b/adapters/advangelists/advangelists.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AdvangelistsAdapter struct { +diff --git a/adapters/advangelists/advangelists_test.go b/adapters/advangelists/advangelists_test.go +index 4219c1a0..49cca96a 100644 +--- a/adapters/advangelists/advangelists_test.go ++++ b/adapters/advangelists/advangelists_test.go +@@ -3,9 +3,9 @@ package advangelists + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/advangelists/params_test.go b/adapters/advangelists/params_test.go +index a58217a0..2a94c782 100644 +--- a/adapters/advangelists/params_test.go ++++ b/adapters/advangelists/params_test.go +@@ -2,7 +2,7 @@ package advangelists + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/advangelists/usersync.go b/adapters/advangelists/usersync.go +index 10c46d18..b1539d00 100644 +--- a/adapters/advangelists/usersync.go ++++ b/adapters/advangelists/usersync.go +@@ -3,8 +3,8 @@ package advangelists + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAdvangelistsSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/advangelists/usersync_test.go b/adapters/advangelists/usersync_test.go +index 2167d49f..5dae85b1 100644 +--- a/adapters/advangelists/usersync_test.go ++++ b/adapters/advangelists/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/aja/aja.go b/adapters/aja/aja.go +index 07f9ad8b..afd9c6d7 100644 +--- a/adapters/aja/aja.go ++++ b/adapters/aja/aja.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type AJAAdapter struct { +diff --git a/adapters/aja/aja_test.go b/adapters/aja/aja_test.go +index bab5419d..d2d9d7fa 100644 +--- a/adapters/aja/aja_test.go ++++ b/adapters/aja/aja_test.go +@@ -3,9 +3,9 @@ package aja + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const testsBidderEndpoint = "https://localhost/bid/4" +diff --git a/adapters/aja/usersync.go b/adapters/aja/usersync.go +index c54405db..deddbabb 100644 +--- a/adapters/aja/usersync.go ++++ b/adapters/aja/usersync.go +@@ -3,8 +3,8 @@ package aja + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAJASyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/aja/usersync_test.go b/adapters/aja/usersync_test.go +index 4b6c90ef..bf03f47a 100644 +--- a/adapters/aja/usersync_test.go ++++ b/adapters/aja/usersync_test.go +@@ -4,10 +4,10 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/amx/amx.go b/adapters/amx/amx.go +index a9dfd06f..ddd0c037 100644 +--- a/adapters/amx/amx.go ++++ b/adapters/amx/amx.go +@@ -7,11 +7,11 @@ import ( + "net/url" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const vastImpressionFormat = "" +diff --git a/adapters/amx/amx_test.go b/adapters/amx/amx_test.go +index 8f05aec2..6fc850cb 100644 +--- a/adapters/amx/amx_test.go ++++ b/adapters/amx/amx_test.go +@@ -6,13 +6,13 @@ import ( + "regexp" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + ) + + const ( +diff --git a/adapters/amx/params_test.go b/adapters/amx/params_test.go +index 89e9a3ad..ef177644 100644 +--- a/adapters/amx/params_test.go ++++ b/adapters/amx/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/amx/usersync.go b/adapters/amx/usersync.go +index 28e6ac0e..d9ff10df 100644 +--- a/adapters/amx/usersync.go ++++ b/adapters/amx/usersync.go +@@ -3,8 +3,8 @@ package amx + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewAMXSyncer produces an AMX RTB usersyncer +diff --git a/adapters/amx/usersync_test.go b/adapters/amx/usersync_test.go +index 20a47c33..e6020b27 100644 +--- a/adapters/amx/usersync_test.go ++++ b/adapters/amx/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/applogy/applogy.go b/adapters/applogy/applogy.go +index ebac0283..cdeafa0f 100644 +--- a/adapters/applogy/applogy.go ++++ b/adapters/applogy/applogy.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ApplogyAdapter struct { +diff --git a/adapters/applogy/applogy_test.go b/adapters/applogy/applogy_test.go +index d86c5cac..63e99ed5 100644 +--- a/adapters/applogy/applogy_test.go ++++ b/adapters/applogy/applogy_test.go +@@ -3,9 +3,9 @@ package applogy + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/appnexus/appnexus.go b/adapters/appnexus/appnexus.go +index 0a8146fd..1e3d4caf 100644 +--- a/adapters/appnexus/appnexus.go ++++ b/adapters/appnexus/appnexus.go +@@ -12,16 +12,16 @@ import ( + "strings" + + "github.com/buger/jsonparser" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + + "golang.org/x/net/context/ctxhttp" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const defaultPlatformID int = 5 +diff --git a/adapters/appnexus/appnexus_test.go b/adapters/appnexus/appnexus_test.go +index d453a779..88b66522 100644 +--- a/adapters/appnexus/appnexus_test.go ++++ b/adapters/appnexus/appnexus_test.go +@@ -13,17 +13,17 @@ import ( + + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/appnexus/params_test.go b/adapters/appnexus/params_test.go +index f84cccc9..c30f5cf3 100644 +--- a/adapters/appnexus/params_test.go ++++ b/adapters/appnexus/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/appnexus.json +diff --git a/adapters/appnexus/usersync.go b/adapters/appnexus/usersync.go +index 22f46f1e..16ffdfa3 100644 +--- a/adapters/appnexus/usersync.go ++++ b/adapters/appnexus/usersync.go +@@ -3,8 +3,8 @@ package appnexus + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAppnexusSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/appnexus/usersync_test.go b/adapters/appnexus/usersync_test.go +index 24b9eede..6796ce13 100644 +--- a/adapters/appnexus/usersync_test.go ++++ b/adapters/appnexus/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go +index 909a03a2..94486e8a 100644 +--- a/adapters/audienceNetwork/facebook.go ++++ b/adapters/audienceNetwork/facebook.go +@@ -10,14 +10,14 @@ import ( + "net/http" + "strings" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/util/maputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/maputil" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + var supportedBannerHeights = map[uint64]bool{ +diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go +index a2e17b71..596529db 100644 +--- a/adapters/audienceNetwork/facebook_test.go ++++ b/adapters/audienceNetwork/facebook_test.go +@@ -4,10 +4,10 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/audienceNetwork/usersync.go b/adapters/audienceNetwork/usersync.go +index 45c1281d..6b8ba3ba 100644 +--- a/adapters/audienceNetwork/usersync.go ++++ b/adapters/audienceNetwork/usersync.go +@@ -3,8 +3,8 @@ package audienceNetwork + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewFacebookSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/audienceNetwork/usersync_test.go b/adapters/audienceNetwork/usersync_test.go +index c3836e4f..e11fb194 100644 +--- a/adapters/audienceNetwork/usersync_test.go ++++ b/adapters/audienceNetwork/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/avocet/avocet.go b/adapters/avocet/avocet.go +index dac7faaa..ef2314e7 100644 +--- a/adapters/avocet/avocet.go ++++ b/adapters/avocet/avocet.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // AvocetAdapter implements a adapters.Bidder compatible with the Avocet advertising platform. +diff --git a/adapters/avocet/avocet_test.go b/adapters/avocet/avocet_test.go +index e4a7c5f4..f669e344 100644 +--- a/adapters/avocet/avocet_test.go ++++ b/adapters/avocet/avocet_test.go +@@ -6,12 +6,12 @@ import ( + "reflect" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/avocet/usersync.go b/adapters/avocet/usersync.go +index f1075ab3..ec4f25dd 100644 +--- a/adapters/avocet/usersync.go ++++ b/adapters/avocet/usersync.go +@@ -3,8 +3,8 @@ package avocet + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewAvocetSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go +index 3df39b77..12b7901c 100644 +--- a/adapters/avocet/usersync_test.go ++++ b/adapters/avocet/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/beachfront/beachfront.go b/adapters/beachfront/beachfront.go +index 1f81eda0..8aca4ca0 100644 +--- a/adapters/beachfront/beachfront.go ++++ b/adapters/beachfront/beachfront.go +@@ -9,11 +9,11 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const Seat = "beachfront" +diff --git a/adapters/beachfront/beachfront_test.go b/adapters/beachfront/beachfront_test.go +index 1c1c4ff4..aace3224 100644 +--- a/adapters/beachfront/beachfront_test.go ++++ b/adapters/beachfront/beachfront_test.go +@@ -3,9 +3,9 @@ package beachfront + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/beachfront/params_test.go b/adapters/beachfront/params_test.go +index ebc0a768..982bd96c 100644 +--- a/adapters/beachfront/params_test.go ++++ b/adapters/beachfront/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/beachfront/usersync.go b/adapters/beachfront/usersync.go +index a6502331..f355697f 100644 +--- a/adapters/beachfront/usersync.go ++++ b/adapters/beachfront/usersync.go +@@ -3,8 +3,8 @@ package beachfront + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + var VENDOR_ID uint16 = 335 +diff --git a/adapters/beachfront/usersync_test.go b/adapters/beachfront/usersync_test.go +index db4d825e..58a072d0 100644 +--- a/adapters/beachfront/usersync_test.go ++++ b/adapters/beachfront/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/beintoo/beintoo.go b/adapters/beintoo/beintoo.go +index 5f22bfa7..9d704c22 100644 +--- a/adapters/beintoo/beintoo.go ++++ b/adapters/beintoo/beintoo.go +@@ -7,11 +7,11 @@ import ( + "net/url" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type BeintooAdapter struct { +diff --git a/adapters/beintoo/beintoo_test.go b/adapters/beintoo/beintoo_test.go +index 181b9562..477d4a54 100644 +--- a/adapters/beintoo/beintoo_test.go ++++ b/adapters/beintoo/beintoo_test.go +@@ -3,9 +3,9 @@ package beintoo + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/beintoo/params_test.go b/adapters/beintoo/params_test.go +index b92b2a10..dca0111f 100644 +--- a/adapters/beintoo/params_test.go ++++ b/adapters/beintoo/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/beintoo/usersync.go b/adapters/beintoo/usersync.go +index a225a2b1..1693c082 100644 +--- a/adapters/beintoo/usersync.go ++++ b/adapters/beintoo/usersync.go +@@ -3,8 +3,8 @@ package beintoo + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewBeintooSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/beintoo/usersync_test.go b/adapters/beintoo/usersync_test.go +index 880d6a84..f0597d8f 100644 +--- a/adapters/beintoo/usersync_test.go ++++ b/adapters/beintoo/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/between/between.go b/adapters/between/between.go +index f8106bdd..038aa4b8 100644 +--- a/adapters/between/between.go ++++ b/adapters/between/between.go +@@ -8,12 +8,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type BetweenAdapter struct { +diff --git a/adapters/between/between_test.go b/adapters/between/between_test.go +index 6471b37b..ee89d663 100644 +--- a/adapters/between/between_test.go ++++ b/adapters/between/between_test.go +@@ -3,9 +3,9 @@ package between + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/between/params_test.go b/adapters/between/params_test.go +index 85771869..599fb95b 100644 +--- a/adapters/between/params_test.go ++++ b/adapters/between/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/between.json +diff --git a/adapters/between/usersync.go b/adapters/between/usersync.go +index a34521fe..bf6a1d32 100644 +--- a/adapters/between/usersync.go ++++ b/adapters/between/usersync.go +@@ -3,8 +3,8 @@ package between + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewBetweenSyncer returns "between" syncer +diff --git a/adapters/between/usersync_test.go b/adapters/between/usersync_test.go +index 6470a9c4..17a51e70 100644 +--- a/adapters/between/usersync_test.go ++++ b/adapters/between/usersync_test.go +@@ -1,7 +1,7 @@ + package between + + import ( +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + "testing" + "text/template" +diff --git a/adapters/bidder.go b/adapters/bidder.go +index 507a065b..e8031291 100644 +--- a/adapters/bidder.go ++++ b/adapters/bidder.go +@@ -5,10 +5,10 @@ import ( + "encoding/json" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Bidder describes how to connect to external demand. +diff --git a/adapters/brightroll/brightroll.go b/adapters/brightroll/brightroll.go +index 0e3fcb06..d1f58682 100644 +--- a/adapters/brightroll/brightroll.go ++++ b/adapters/brightroll/brightroll.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type BrightrollAdapter struct { +diff --git a/adapters/brightroll/brightroll_test.go b/adapters/brightroll/brightroll_test.go +index a41a9e79..c25c57ed 100644 +--- a/adapters/brightroll/brightroll_test.go ++++ b/adapters/brightroll/brightroll_test.go +@@ -5,9 +5,9 @@ import ( + + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestEmptyConfig(t *testing.T) { +diff --git a/adapters/brightroll/params_test.go b/adapters/brightroll/params_test.go +index beac822f..6c65b8ce 100644 +--- a/adapters/brightroll/params_test.go ++++ b/adapters/brightroll/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/brightroll.json +diff --git a/adapters/brightroll/usersync.go b/adapters/brightroll/usersync.go +index b33cc5a2..74729f70 100644 +--- a/adapters/brightroll/usersync.go ++++ b/adapters/brightroll/usersync.go +@@ -3,8 +3,8 @@ package brightroll + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewBrightrollSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/brightroll/usersync_test.go b/adapters/brightroll/usersync_test.go +index a5d47e35..1ca03254 100644 +--- a/adapters/brightroll/usersync_test.go ++++ b/adapters/brightroll/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/colossus/colossus.go b/adapters/colossus/colossus.go +index 4a5360ce..8f7aa792 100644 +--- a/adapters/colossus/colossus.go ++++ b/adapters/colossus/colossus.go +@@ -6,11 +6,11 @@ import ( + "net/http" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ColossusAdapter struct { +diff --git a/adapters/colossus/colossus_test.go b/adapters/colossus/colossus_test.go +index 7baa423a..25d107a5 100644 +--- a/adapters/colossus/colossus_test.go ++++ b/adapters/colossus/colossus_test.go +@@ -3,9 +3,9 @@ package colossus + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/colossus/params_test.go b/adapters/colossus/params_test.go +index 2883de2f..c223209e 100644 +--- a/adapters/colossus/params_test.go ++++ b/adapters/colossus/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the colossus schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/colossus/usersync.go b/adapters/colossus/usersync.go +index a4e82ee3..519dcaba 100644 +--- a/adapters/colossus/usersync.go ++++ b/adapters/colossus/usersync.go +@@ -3,8 +3,8 @@ package colossus + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewColossusSyncer returns colossus syncer +diff --git a/adapters/colossus/usersync_test.go b/adapters/colossus/usersync_test.go +index 52eb6389..5bbbdc08 100644 +--- a/adapters/colossus/usersync_test.go ++++ b/adapters/colossus/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/connectad/connectad.go b/adapters/connectad/connectad.go +index 024dabce..7731e0fd 100644 +--- a/adapters/connectad/connectad.go ++++ b/adapters/connectad/connectad.go +@@ -7,11 +7,11 @@ import ( + "net/url" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ConnectAdAdapter struct { +diff --git a/adapters/connectad/connectad_test.go b/adapters/connectad/connectad_test.go +index 36005219..58984e90 100644 +--- a/adapters/connectad/connectad_test.go ++++ b/adapters/connectad/connectad_test.go +@@ -3,9 +3,9 @@ package connectad + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/connectad/params_test.go b/adapters/connectad/params_test.go +index 6d55b1ce..c8c407f8 100644 +--- a/adapters/connectad/params_test.go ++++ b/adapters/connectad/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/connectad/usersync.go b/adapters/connectad/usersync.go +index 5661cb5d..ccd2d52c 100644 +--- a/adapters/connectad/usersync.go ++++ b/adapters/connectad/usersync.go +@@ -3,8 +3,8 @@ package connectad + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewConnectAdSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/connectad/usersync_test.go b/adapters/connectad/usersync_test.go +index c4b4962b..c7b5c7c3 100644 +--- a/adapters/connectad/usersync_test.go ++++ b/adapters/connectad/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/consumable/adtypes.go b/adapters/consumable/adtypes.go +index 5eb3d3b3..f04f8822 100644 +--- a/adapters/consumable/adtypes.go ++++ b/adapters/consumable/adtypes.go +@@ -1,7 +1,7 @@ + package consumable + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "strconv" + ) + +diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go +index 15511c0e..e6aea9c5 100644 +--- a/adapters/consumable/consumable.go ++++ b/adapters/consumable/consumable.go +@@ -8,12 +8,12 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + ) + + type ConsumableAdapter struct { +diff --git a/adapters/consumable/consumable_test.go b/adapters/consumable/consumable_test.go +index 56e1d626..63f5d929 100644 +--- a/adapters/consumable/consumable_test.go ++++ b/adapters/consumable/consumable_test.go +@@ -4,10 +4,10 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/consumable/params_test.go b/adapters/consumable/params_test.go +index 42de5cb9..9f922796 100644 +--- a/adapters/consumable/params_test.go ++++ b/adapters/consumable/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/consumable.json +diff --git a/adapters/consumable/usersync.go b/adapters/consumable/usersync.go +index 0e0938c7..720b0bea 100644 +--- a/adapters/consumable/usersync.go ++++ b/adapters/consumable/usersync.go +@@ -3,8 +3,8 @@ package consumable + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + var VENDOR_ID uint16 = 591 +diff --git a/adapters/consumable/usersync_test.go b/adapters/consumable/usersync_test.go +index ef71c0b1..1aeb2eb8 100644 +--- a/adapters/consumable/usersync_test.go ++++ b/adapters/consumable/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/conversant/cnvr_legacy.go b/adapters/conversant/cnvr_legacy.go +index 4672ee15..d330698e 100644 +--- a/adapters/conversant/cnvr_legacy.go ++++ b/adapters/conversant/cnvr_legacy.go +@@ -8,10 +8,10 @@ import ( + "io/ioutil" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/conversant/cnvr_legacy_test.go b/adapters/conversant/cnvr_legacy_test.go +index 712f85f4..cfa5bc7b 100644 +--- a/adapters/conversant/cnvr_legacy_test.go ++++ b/adapters/conversant/cnvr_legacy_test.go +@@ -11,12 +11,12 @@ import ( + "testing" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // Constants +diff --git a/adapters/conversant/conversant.go b/adapters/conversant/conversant.go +index 248aa200..fe4e6602 100644 +--- a/adapters/conversant/conversant.go ++++ b/adapters/conversant/conversant.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ConversantAdapter struct { +diff --git a/adapters/conversant/conversant_test.go b/adapters/conversant/conversant_test.go +index 55b316b4..2de1facf 100644 +--- a/adapters/conversant/conversant_test.go ++++ b/adapters/conversant/conversant_test.go +@@ -3,9 +3,9 @@ package conversant + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/conversant/usersync.go b/adapters/conversant/usersync.go +index c2676df6..3fe0a45c 100644 +--- a/adapters/conversant/usersync.go ++++ b/adapters/conversant/usersync.go +@@ -3,8 +3,8 @@ package conversant + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewConversantSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/conversant/usersync_test.go b/adapters/conversant/usersync_test.go +index 16affbd1..ef05454b 100644 +--- a/adapters/conversant/usersync_test.go ++++ b/adapters/conversant/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/cpmstar/cpmstar.go b/adapters/cpmstar/cpmstar.go +index f3fbdf70..d7bbb576 100644 +--- a/adapters/cpmstar/cpmstar.go ++++ b/adapters/cpmstar/cpmstar.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type Adapter struct { +diff --git a/adapters/cpmstar/cpmstar_test.go b/adapters/cpmstar/cpmstar_test.go +index c10dfbe1..8d214e4e 100644 +--- a/adapters/cpmstar/cpmstar_test.go ++++ b/adapters/cpmstar/cpmstar_test.go +@@ -3,9 +3,9 @@ package cpmstar + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/cpmstar/params_test.go b/adapters/cpmstar/params_test.go +index cee471a8..dec3f342 100644 +--- a/adapters/cpmstar/params_test.go ++++ b/adapters/cpmstar/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/cpmstar.json +diff --git a/adapters/cpmstar/usersync.go b/adapters/cpmstar/usersync.go +index 9c864e24..91d3c89c 100644 +--- a/adapters/cpmstar/usersync.go ++++ b/adapters/cpmstar/usersync.go +@@ -3,8 +3,8 @@ package cpmstar + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + //NewCpmstarSyncer : +diff --git a/adapters/cpmstar/usersync_test.go b/adapters/cpmstar/usersync_test.go +index dae55e63..cd04838a 100644 +--- a/adapters/cpmstar/usersync_test.go ++++ b/adapters/cpmstar/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/datablocks/datablocks.go b/adapters/datablocks/datablocks.go +index 56ac8f68..84380184 100644 +--- a/adapters/datablocks/datablocks.go ++++ b/adapters/datablocks/datablocks.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type DatablocksAdapter struct { +diff --git a/adapters/datablocks/datablocks_test.go b/adapters/datablocks/datablocks_test.go +index 553c8edf..1ee06b93 100644 +--- a/adapters/datablocks/datablocks_test.go ++++ b/adapters/datablocks/datablocks_test.go +@@ -3,9 +3,9 @@ package datablocks + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/datablocks/usersync.go b/adapters/datablocks/usersync.go +index 2b47b259..3993215b 100644 +--- a/adapters/datablocks/usersync.go ++++ b/adapters/datablocks/usersync.go +@@ -3,8 +3,8 @@ package datablocks + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const datablocksGDPRVendorID = uint16(0) +diff --git a/adapters/datablocks/usersync_test.go b/adapters/datablocks/usersync_test.go +index a7518e9b..3ab7ae1b 100644 +--- a/adapters/datablocks/usersync_test.go ++++ b/adapters/datablocks/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/decenterads/decenterads.go b/adapters/decenterads/decenterads.go +index 5719bf1e..c0ba2eb9 100644 +--- a/adapters/decenterads/decenterads.go ++++ b/adapters/decenterads/decenterads.go +@@ -7,11 +7,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type adapter struct { +diff --git a/adapters/decenterads/decenterads_test.go b/adapters/decenterads/decenterads_test.go +index ca86e891..eed77d7a 100644 +--- a/adapters/decenterads/decenterads_test.go ++++ b/adapters/decenterads/decenterads_test.go +@@ -3,9 +3,9 @@ package decenterads + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/decenterads/params_test.go b/adapters/decenterads/params_test.go +index 3d3708be..fcf5e94f 100644 +--- a/adapters/decenterads/params_test.go ++++ b/adapters/decenterads/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the decenterads schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/deepintent/deepintent.go b/adapters/deepintent/deepintent.go +index 1ddaa156..5a9b30c9 100644 +--- a/adapters/deepintent/deepintent.go ++++ b/adapters/deepintent/deepintent.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const displayManager string = "di_prebid" +diff --git a/adapters/deepintent/deepintent_test.go b/adapters/deepintent/deepintent_test.go +index cdf158e6..a0dbf836 100644 +--- a/adapters/deepintent/deepintent_test.go ++++ b/adapters/deepintent/deepintent_test.go +@@ -3,10 +3,10 @@ package deepintent + import ( + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/prebid/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/deepintent/params_test.go b/adapters/deepintent/params_test.go +index 8f37e5a9..87f80dfd 100644 +--- a/adapters/deepintent/params_test.go ++++ b/adapters/deepintent/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the deepintent schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/deepintent/usersync.go b/adapters/deepintent/usersync.go +index 9e803df6..8ab615e3 100644 +--- a/adapters/deepintent/usersync.go ++++ b/adapters/deepintent/usersync.go +@@ -3,8 +3,8 @@ package deepintent + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewDeepintentSyncer returns deepintent syncer +diff --git a/adapters/deepintent/usersync_test.go b/adapters/deepintent/usersync_test.go +index 06a221ca..1e0174ab 100644 +--- a/adapters/deepintent/usersync_test.go ++++ b/adapters/deepintent/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/dmx/dmx.go b/adapters/dmx/dmx.go +index c66c6bf3..aa6163ad 100644 +--- a/adapters/dmx/dmx.go ++++ b/adapters/dmx/dmx.go +@@ -8,11 +8,11 @@ import ( + "net/url" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type DmxAdapter struct { +diff --git a/adapters/dmx/dmx_test.go b/adapters/dmx/dmx_test.go +index 80498de6..0649cb8a 100644 +--- a/adapters/dmx/dmx_test.go ++++ b/adapters/dmx/dmx_test.go +@@ -5,12 +5,12 @@ import ( + "strings" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/prebid/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" + ) + + var ( +diff --git a/adapters/dmx/params_test.go b/adapters/dmx/params_test.go +index 0e5250b1..4d9da786 100644 +--- a/adapters/dmx/params_test.go ++++ b/adapters/dmx/params_test.go +@@ -2,7 +2,7 @@ package dmx + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/dmx/usersync.go b/adapters/dmx/usersync.go +index 98e56234..07c70ff9 100644 +--- a/adapters/dmx/usersync.go ++++ b/adapters/dmx/usersync.go +@@ -3,8 +3,8 @@ package dmx + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewDmxSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/dmx/usersync_test.go b/adapters/dmx/usersync_test.go +index e4e3c7d8..2aa66575 100644 +--- a/adapters/dmx/usersync_test.go ++++ b/adapters/dmx/usersync_test.go +@@ -1,7 +1,7 @@ + package dmx + + import ( +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "testing" + "text/template" + +diff --git a/adapters/emx_digital/emx_digital.go b/adapters/emx_digital/emx_digital.go +index e87f0681..dcebfdcb 100644 +--- a/adapters/emx_digital/emx_digital.go ++++ b/adapters/emx_digital/emx_digital.go +@@ -9,11 +9,11 @@ import ( + "strings" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type EmxDigitalAdapter struct { +diff --git a/adapters/emx_digital/emx_digital_test.go b/adapters/emx_digital/emx_digital_test.go +index bb7567e8..3e0e52e2 100644 +--- a/adapters/emx_digital/emx_digital_test.go ++++ b/adapters/emx_digital/emx_digital_test.go +@@ -3,10 +3,10 @@ package emx_digital + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/emx_digital/params_test.go b/adapters/emx_digital/params_test.go +index 49ad9eb1..9a6ff5cf 100644 +--- a/adapters/emx_digital/params_test.go ++++ b/adapters/emx_digital/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/emx_digital/usersync.go b/adapters/emx_digital/usersync.go +index a453955b..38b6377f 100644 +--- a/adapters/emx_digital/usersync.go ++++ b/adapters/emx_digital/usersync.go +@@ -3,8 +3,8 @@ package emx_digital + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewEMXDigitalSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/emx_digital/usersync_test.go b/adapters/emx_digital/usersync_test.go +index 59d66d87..15bdd738 100644 +--- a/adapters/emx_digital/usersync_test.go ++++ b/adapters/emx_digital/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/engagebdr/engagebdr.go b/adapters/engagebdr/engagebdr.go +index 16d20afa..a2401cd7 100644 +--- a/adapters/engagebdr/engagebdr.go ++++ b/adapters/engagebdr/engagebdr.go +@@ -4,14 +4,14 @@ import ( + "encoding/json" + "net/http" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + ) + + type EngageBDRAdapter struct { +diff --git a/adapters/engagebdr/engagebdr_test.go b/adapters/engagebdr/engagebdr_test.go +index 97e6a8e9..699ef446 100644 +--- a/adapters/engagebdr/engagebdr_test.go ++++ b/adapters/engagebdr/engagebdr_test.go +@@ -3,9 +3,9 @@ package engagebdr + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/engagebdr/params_test.go b/adapters/engagebdr/params_test.go +index c797d04e..b6c887ea 100644 +--- a/adapters/engagebdr/params_test.go ++++ b/adapters/engagebdr/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/engagebdr/usersync.go b/adapters/engagebdr/usersync.go +index ae4047aa..99b6a122 100644 +--- a/adapters/engagebdr/usersync.go ++++ b/adapters/engagebdr/usersync.go +@@ -1,8 +1,8 @@ + package engagebdr + + import ( +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "text/template" + ) + +diff --git a/adapters/engagebdr/usersync_test.go b/adapters/engagebdr/usersync_test.go +index 3a6c179a..588f1f97 100644 +--- a/adapters/engagebdr/usersync_test.go ++++ b/adapters/engagebdr/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/eplanning/eplanning.go b/adapters/eplanning/eplanning.go +index 8d0f7e8b..062dcff4 100644 +--- a/adapters/eplanning/eplanning.go ++++ b/adapters/eplanning/eplanning.go +@@ -11,11 +11,11 @@ import ( + + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "strconv" + ) +diff --git a/adapters/eplanning/eplanning_test.go b/adapters/eplanning/eplanning_test.go +index 70e108d5..bf432788 100644 +--- a/adapters/eplanning/eplanning_test.go ++++ b/adapters/eplanning/eplanning_test.go +@@ -3,10 +3,10 @@ package eplanning + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/eplanning/usersync.go b/adapters/eplanning/usersync.go +index faa7fa82..4f9f30ae 100644 +--- a/adapters/eplanning/usersync.go ++++ b/adapters/eplanning/usersync.go +@@ -3,8 +3,8 @@ package eplanning + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewEPlanningSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/eplanning/usersync_test.go b/adapters/eplanning/usersync_test.go +index 85770689..dd0bf152 100644 +--- a/adapters/eplanning/usersync_test.go ++++ b/adapters/eplanning/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/gamma/gamma.go b/adapters/gamma/gamma.go +index f8681fb9..47f5775f 100644 +--- a/adapters/gamma/gamma.go ++++ b/adapters/gamma/gamma.go +@@ -7,11 +7,11 @@ import ( + "net/url" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type GammaAdapter struct { +diff --git a/adapters/gamma/gamma_test.go b/adapters/gamma/gamma_test.go +index 2741e425..fb648cd4 100644 +--- a/adapters/gamma/gamma_test.go ++++ b/adapters/gamma/gamma_test.go +@@ -3,9 +3,9 @@ package gamma + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/gamma/params_test.go b/adapters/gamma/params_test.go +index 3329545a..5e096cbf 100644 +--- a/adapters/gamma/params_test.go ++++ b/adapters/gamma/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/gamma.json +diff --git a/adapters/gamma/usersync.go b/adapters/gamma/usersync.go +index f19c522c..884468fe 100644 +--- a/adapters/gamma/usersync.go ++++ b/adapters/gamma/usersync.go +@@ -3,8 +3,8 @@ package gamma + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewGammaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/gamma/usersync_test.go b/adapters/gamma/usersync_test.go +index 4278f9ab..04a88e5e 100644 +--- a/adapters/gamma/usersync_test.go ++++ b/adapters/gamma/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/gamoshi/gamoshi.go b/adapters/gamoshi/gamoshi.go +index c0791e66..96940c97 100644 +--- a/adapters/gamoshi/gamoshi.go ++++ b/adapters/gamoshi/gamoshi.go +@@ -7,11 +7,11 @@ import ( + "strconv" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type GamoshiAdapter struct { +diff --git a/adapters/gamoshi/gamoshi_test.go b/adapters/gamoshi/gamoshi_test.go +index d8834c45..dc8c6091 100644 +--- a/adapters/gamoshi/gamoshi_test.go ++++ b/adapters/gamoshi/gamoshi_test.go +@@ -3,9 +3,9 @@ package gamoshi + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamplesWithConfiguredURI(t *testing.T) { +diff --git a/adapters/gamoshi/params_test.go b/adapters/gamoshi/params_test.go +index 29a1864b..d33740ee 100644 +--- a/adapters/gamoshi/params_test.go ++++ b/adapters/gamoshi/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/gamoshi.json +diff --git a/adapters/gamoshi/usersync.go b/adapters/gamoshi/usersync.go +index 6b7c43dd..73101ada 100644 +--- a/adapters/gamoshi/usersync.go ++++ b/adapters/gamoshi/usersync.go +@@ -3,8 +3,8 @@ package gamoshi + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewGamoshiSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/gamoshi/usersync_test.go b/adapters/gamoshi/usersync_test.go +index 43dc88a4..08ddd534 100644 +--- a/adapters/gamoshi/usersync_test.go ++++ b/adapters/gamoshi/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/grid/grid.go b/adapters/grid/grid.go +index e70b1ddd..955ead6b 100644 +--- a/adapters/grid/grid.go ++++ b/adapters/grid/grid.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type GridAdapter struct { +diff --git a/adapters/grid/grid_test.go b/adapters/grid/grid_test.go +index 1f9baaba..8438bd28 100644 +--- a/adapters/grid/grid_test.go ++++ b/adapters/grid/grid_test.go +@@ -3,9 +3,9 @@ package grid + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/grid/usersync.go b/adapters/grid/usersync.go +index afdc5db7..52d25405 100644 +--- a/adapters/grid/usersync.go ++++ b/adapters/grid/usersync.go +@@ -3,8 +3,8 @@ package grid + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewGridSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/grid/usersync_test.go b/adapters/grid/usersync_test.go +index 99730b5d..40cb443f 100644 +--- a/adapters/grid/usersync_test.go ++++ b/adapters/grid/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/gumgum/gumgum.go b/adapters/gumgum/gumgum.go +index 9b26f8f8..acb08ea3 100644 +--- a/adapters/gumgum/gumgum.go ++++ b/adapters/gumgum/gumgum.go +@@ -7,11 +7,11 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // GumGumAdapter implements Bidder interface. +diff --git a/adapters/gumgum/gumgum_test.go b/adapters/gumgum/gumgum_test.go +index b097042b..fc2d1059 100644 +--- a/adapters/gumgum/gumgum_test.go ++++ b/adapters/gumgum/gumgum_test.go +@@ -3,9 +3,9 @@ package gumgum + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/gumgum/params_test.go b/adapters/gumgum/params_test.go +index 4cb6f019..087c9136 100644 +--- a/adapters/gumgum/params_test.go ++++ b/adapters/gumgum/params_test.go +@@ -2,7 +2,7 @@ package gumgum + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/gumgum/usersync.go b/adapters/gumgum/usersync.go +index 5d29c7dc..b56b9d73 100644 +--- a/adapters/gumgum/usersync.go ++++ b/adapters/gumgum/usersync.go +@@ -3,8 +3,8 @@ package gumgum + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewGumGumSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/gumgum/usersync_test.go b/adapters/gumgum/usersync_test.go +index 9c6dc420..47301f9c 100644 +--- a/adapters/gumgum/usersync_test.go ++++ b/adapters/gumgum/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go +index 0d5c043e..acda1b06 100644 +--- a/adapters/improvedigital/improvedigital.go ++++ b/adapters/improvedigital/improvedigital.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ImprovedigitalAdapter struct { +diff --git a/adapters/improvedigital/improvedigital_test.go b/adapters/improvedigital/improvedigital_test.go +index 7a45faac..b4530882 100644 +--- a/adapters/improvedigital/improvedigital_test.go ++++ b/adapters/improvedigital/improvedigital_test.go +@@ -3,9 +3,9 @@ package improvedigital + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/improvedigital/params_test.go b/adapters/improvedigital/params_test.go +index 13bdd807..f6034797 100644 +--- a/adapters/improvedigital/params_test.go ++++ b/adapters/improvedigital/params_test.go +@@ -2,7 +2,7 @@ package improvedigital + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/improvedigital/usersync.go b/adapters/improvedigital/usersync.go +index 72c3ddaf..9b5f7e89 100644 +--- a/adapters/improvedigital/usersync.go ++++ b/adapters/improvedigital/usersync.go +@@ -3,8 +3,8 @@ package improvedigital + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewImprovedigitalSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/improvedigital/usersync_test.go b/adapters/improvedigital/usersync_test.go +index 35ea89cf..91c6cd91 100644 +--- a/adapters/improvedigital/usersync_test.go ++++ b/adapters/improvedigital/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/info.go b/adapters/info.go +index 19897ac7..5f5f6d96 100644 +--- a/adapters/info.go ++++ b/adapters/info.go +@@ -6,10 +6,10 @@ import ( + "strings" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + yaml "gopkg.in/yaml.v2" + ) + +diff --git a/adapters/info_test.go b/adapters/info_test.go +index ce0f8869..2d1f84cb 100644 +--- a/adapters/info_test.go ++++ b/adapters/info_test.go +@@ -5,11 +5,11 @@ import ( + "strings" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/inmobi/inmobi.go b/adapters/inmobi/inmobi.go +index 5d978ec7..411988fc 100644 +--- a/adapters/inmobi/inmobi.go ++++ b/adapters/inmobi/inmobi.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type InMobiAdapter struct { +diff --git a/adapters/inmobi/inmobi_test.go b/adapters/inmobi/inmobi_test.go +index 0b662684..bfe5d10b 100644 +--- a/adapters/inmobi/inmobi_test.go ++++ b/adapters/inmobi/inmobi_test.go +@@ -3,9 +3,9 @@ package inmobi + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/invibes/invibes.go b/adapters/invibes/invibes.go +index 31124bd1..358b1626 100644 +--- a/adapters/invibes/invibes.go ++++ b/adapters/invibes/invibes.go +@@ -9,13 +9,13 @@ import ( + "strings" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const adapterVersion = "prebid_1.0.0" +diff --git a/adapters/invibes/invibes_test.go b/adapters/invibes/invibes_test.go +index 73d6ec0b..47071d47 100644 +--- a/adapters/invibes/invibes_test.go ++++ b/adapters/invibes/invibes_test.go +@@ -3,9 +3,9 @@ package invibes + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/invibes/params_test.go b/adapters/invibes/params_test.go +index 8c3a26b4..84d1d049 100644 +--- a/adapters/invibes/params_test.go ++++ b/adapters/invibes/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/invibes/usersync.go b/adapters/invibes/usersync.go +index 468e604c..c334f8ff 100644 +--- a/adapters/invibes/usersync.go ++++ b/adapters/invibes/usersync.go +@@ -3,8 +3,8 @@ package invibes + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewInvibesSyncer(urlTemplate *template.Template) usersync.Usersyncer { +diff --git a/adapters/invibes/usersync_test.go b/adapters/invibes/usersync_test.go +index 3f715f6b..e575daf2 100644 +--- a/adapters/invibes/usersync_test.go ++++ b/adapters/invibes/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go +index 96cd988d..e0a571aa 100644 +--- a/adapters/ix/ix.go ++++ b/adapters/ix/ix.go +@@ -10,12 +10,12 @@ import ( + + "golang.org/x/net/context/ctxhttp" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + ) + + type IxAdapter struct { +diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go +index 8a75e685..aaee4ba7 100644 +--- a/adapters/ix/ix_test.go ++++ b/adapters/ix/ix_test.go +@@ -11,12 +11,12 @@ import ( + "testing" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + ) + + const endpoint string = "http://host/endpoint" +diff --git a/adapters/ix/usersync.go b/adapters/ix/usersync.go +index 6f355894..dcb4d646 100644 +--- a/adapters/ix/usersync.go ++++ b/adapters/ix/usersync.go +@@ -3,8 +3,8 @@ package ix + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewIxSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/ix/usersync_test.go b/adapters/ix/usersync_test.go +index 10f91da1..142ee6ac 100644 +--- a/adapters/ix/usersync_test.go ++++ b/adapters/ix/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/kidoz/kidoz.go b/adapters/kidoz/kidoz.go +index fda6869e..755ffd24 100644 +--- a/adapters/kidoz/kidoz.go ++++ b/adapters/kidoz/kidoz.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type KidozAdapter struct { +diff --git a/adapters/kidoz/kidoz_test.go b/adapters/kidoz/kidoz_test.go +index 5830f60d..b3365ac7 100644 +--- a/adapters/kidoz/kidoz_test.go ++++ b/adapters/kidoz/kidoz_test.go +@@ -5,11 +5,11 @@ import ( + "net/http" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/kidoz/params_test.go b/adapters/kidoz/params_test.go +index 073d7382..43c5a68d 100644 +--- a/adapters/kidoz/params_test.go ++++ b/adapters/kidoz/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/krushmedia/krushmedia.go b/adapters/krushmedia/krushmedia.go +index d05b6e79..1f2e6b63 100644 +--- a/adapters/krushmedia/krushmedia.go ++++ b/adapters/krushmedia/krushmedia.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type KrushmediaAdapter struct { +diff --git a/adapters/krushmedia/krushmedia_test.go b/adapters/krushmedia/krushmedia_test.go +index 7bdea503..0fc5c93f 100644 +--- a/adapters/krushmedia/krushmedia_test.go ++++ b/adapters/krushmedia/krushmedia_test.go +@@ -3,9 +3,9 @@ package krushmedia + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/krushmedia/params_test.go b/adapters/krushmedia/params_test.go +index 26daa56e..0f912513 100644 +--- a/adapters/krushmedia/params_test.go ++++ b/adapters/krushmedia/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + var validParams = []string{ +diff --git a/adapters/krushmedia/usersync.go b/adapters/krushmedia/usersync.go +index 5dc1471f..0a4f664e 100644 +--- a/adapters/krushmedia/usersync.go ++++ b/adapters/krushmedia/usersync.go +@@ -3,8 +3,8 @@ package krushmedia + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewKrushmediaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/krushmedia/usersync_test.go b/adapters/krushmedia/usersync_test.go +index b58f2f1b..a5908b80 100644 +--- a/adapters/krushmedia/usersync_test.go ++++ b/adapters/krushmedia/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/kubient/kubient.go b/adapters/kubient/kubient.go +index a9955865..fd6c4ef1 100644 +--- a/adapters/kubient/kubient.go ++++ b/adapters/kubient/kubient.go +@@ -5,12 +5,12 @@ import ( + "fmt" + "net/http" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + ) + + // Builder builds a new instance of the Kubient adapter for the given bidder with the given config. +diff --git a/adapters/kubient/kubient_test.go b/adapters/kubient/kubient_test.go +index 19eb3e8f..e7f8a9ee 100644 +--- a/adapters/kubient/kubient_test.go ++++ b/adapters/kubient/kubient_test.go +@@ -3,9 +3,9 @@ package kubient + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/legacy.go b/adapters/legacy.go +index 8b2221fe..c3fde16d 100644 +--- a/adapters/legacy.go ++++ b/adapters/legacy.go +@@ -6,8 +6,8 @@ import ( + "net/http" + "time" + +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/server/ssl" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" + ) + + // This file contains some deprecated, legacy types. +diff --git a/adapters/lifestreet/lifestreet.go b/adapters/lifestreet/lifestreet.go +index 4dbd7bae..ca9b9688 100644 +--- a/adapters/lifestreet/lifestreet.go ++++ b/adapters/lifestreet/lifestreet.go +@@ -9,12 +9,12 @@ import ( + "net/http" + "strings" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/lifestreet/lifestreet_test.go b/adapters/lifestreet/lifestreet_test.go +index 412333c3..39712192 100644 +--- a/adapters/lifestreet/lifestreet_test.go ++++ b/adapters/lifestreet/lifestreet_test.go +@@ -10,15 +10,15 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + type lsTagInfo struct { +diff --git a/adapters/lifestreet/usersync.go b/adapters/lifestreet/usersync.go +index 15ffde4d..4f18854e 100644 +--- a/adapters/lifestreet/usersync.go ++++ b/adapters/lifestreet/usersync.go +@@ -3,8 +3,8 @@ package lifestreet + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewLifestreetSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/lifestreet/usersync_test.go b/adapters/lifestreet/usersync_test.go +index 8fee0935..134af2f5 100644 +--- a/adapters/lifestreet/usersync_test.go ++++ b/adapters/lifestreet/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/lockerdome/lockerdome.go b/adapters/lockerdome/lockerdome.go +index a81313c6..ec9ebe8c 100644 +--- a/adapters/lockerdome/lockerdome.go ++++ b/adapters/lockerdome/lockerdome.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const unexpectedStatusCodeMessage = "Unexpected status code: %d. Run with request.debug = 1 for more info" +diff --git a/adapters/lockerdome/lockerdome_test.go b/adapters/lockerdome/lockerdome_test.go +index 6ac495d5..ca3da099 100644 +--- a/adapters/lockerdome/lockerdome_test.go ++++ b/adapters/lockerdome/lockerdome_test.go +@@ -3,9 +3,9 @@ package lockerdome + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/lockerdome/params_test.go b/adapters/lockerdome/params_test.go +index 81524657..61ee259a 100644 +--- a/adapters/lockerdome/params_test.go ++++ b/adapters/lockerdome/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file tests static/bidder-params/lockerdome.json +diff --git a/adapters/lockerdome/usersync.go b/adapters/lockerdome/usersync.go +index fb01f8e2..67c4e15d 100644 +--- a/adapters/lockerdome/usersync.go ++++ b/adapters/lockerdome/usersync.go +@@ -3,8 +3,8 @@ package lockerdome + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewLockerDomeSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/lockerdome/usersync_test.go b/adapters/lockerdome/usersync_test.go +index b5dea226..acfa788e 100644 +--- a/adapters/lockerdome/usersync_test.go ++++ b/adapters/lockerdome/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/logicad/logicad.go b/adapters/logicad/logicad.go +index 30d9dd5d..dd316321 100644 +--- a/adapters/logicad/logicad.go ++++ b/adapters/logicad/logicad.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type LogicadAdapter struct { +diff --git a/adapters/logicad/logicad_test.go b/adapters/logicad/logicad_test.go +index 820aad97..5da8e56e 100644 +--- a/adapters/logicad/logicad_test.go ++++ b/adapters/logicad/logicad_test.go +@@ -3,9 +3,9 @@ package logicad + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/logicad/params_test.go b/adapters/logicad/params_test.go +index eb344528..8b5d296b 100644 +--- a/adapters/logicad/params_test.go ++++ b/adapters/logicad/params_test.go +@@ -2,7 +2,7 @@ package logicad + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/logicad/usersync.go b/adapters/logicad/usersync.go +index d26a197b..34d29a25 100644 +--- a/adapters/logicad/usersync.go ++++ b/adapters/logicad/usersync.go +@@ -3,8 +3,8 @@ package logicad + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewLogicadSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/logicad/usersync_test.go b/adapters/logicad/usersync_test.go +index 89d6207d..aeb029d3 100644 +--- a/adapters/logicad/usersync_test.go ++++ b/adapters/logicad/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/lunamedia/lunamedia.go b/adapters/lunamedia/lunamedia.go +index 289d062c..c5e0ef8a 100644 +--- a/adapters/lunamedia/lunamedia.go ++++ b/adapters/lunamedia/lunamedia.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type LunaMediaAdapter struct { +diff --git a/adapters/lunamedia/lunamedia_test.go b/adapters/lunamedia/lunamedia_test.go +index 4149060c..6d0952cd 100644 +--- a/adapters/lunamedia/lunamedia_test.go ++++ b/adapters/lunamedia/lunamedia_test.go +@@ -3,9 +3,9 @@ package lunamedia + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/lunamedia/params_test.go b/adapters/lunamedia/params_test.go +index b4faeea1..2f21ea45 100644 +--- a/adapters/lunamedia/params_test.go ++++ b/adapters/lunamedia/params_test.go +@@ -2,7 +2,7 @@ package lunamedia + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/lunamedia/usersync.go b/adapters/lunamedia/usersync.go +index 7ad54e38..194c4b77 100644 +--- a/adapters/lunamedia/usersync.go ++++ b/adapters/lunamedia/usersync.go +@@ -3,8 +3,8 @@ package lunamedia + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewLunaMediaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/lunamedia/usersync_test.go b/adapters/lunamedia/usersync_test.go +index c9fe2032..3a549aec 100644 +--- a/adapters/lunamedia/usersync_test.go ++++ b/adapters/lunamedia/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/marsmedia/marsmedia.go b/adapters/marsmedia/marsmedia.go +index eb1e8da0..f1cffac2 100644 +--- a/adapters/marsmedia/marsmedia.go ++++ b/adapters/marsmedia/marsmedia.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type MarsmediaAdapter struct { +diff --git a/adapters/marsmedia/marsmedia_test.go b/adapters/marsmedia/marsmedia_test.go +index ab87bf77..03e46312 100644 +--- a/adapters/marsmedia/marsmedia_test.go ++++ b/adapters/marsmedia/marsmedia_test.go +@@ -3,9 +3,9 @@ package marsmedia + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/marsmedia/params_test.go b/adapters/marsmedia/params_test.go +index 43cd49c2..2e3b4838 100644 +--- a/adapters/marsmedia/params_test.go ++++ b/adapters/marsmedia/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/marsmedia.json +diff --git a/adapters/marsmedia/usersync.go b/adapters/marsmedia/usersync.go +index 50996f32..63d06d9d 100644 +--- a/adapters/marsmedia/usersync.go ++++ b/adapters/marsmedia/usersync.go +@@ -3,8 +3,8 @@ package marsmedia + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewMarsmediaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/marsmedia/usersync_test.go b/adapters/marsmedia/usersync_test.go +index f019c014..cc4f0b81 100644 +--- a/adapters/marsmedia/usersync_test.go ++++ b/adapters/marsmedia/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/mediafuse/usersync.go b/adapters/mediafuse/usersync.go +index 5381bf36..ca299c72 100644 +--- a/adapters/mediafuse/usersync.go ++++ b/adapters/mediafuse/usersync.go +@@ -3,8 +3,8 @@ package mediafuse + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewMediafuseSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/mediafuse/usersync_test.go b/adapters/mediafuse/usersync_test.go +index 30b5b535..95e6a7cf 100644 +--- a/adapters/mediafuse/usersync_test.go ++++ b/adapters/mediafuse/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/mgid/mgid.go b/adapters/mgid/mgid.go +index d6ba175d..9754ba80 100644 +--- a/adapters/mgid/mgid.go ++++ b/adapters/mgid/mgid.go +@@ -6,11 +6,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type MgidAdapter struct { +diff --git a/adapters/mgid/mgid_test.go b/adapters/mgid/mgid_test.go +index 7d300451..11f5596e 100644 +--- a/adapters/mgid/mgid_test.go ++++ b/adapters/mgid/mgid_test.go +@@ -3,9 +3,9 @@ package mgid + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/mgid/usersync.go b/adapters/mgid/usersync.go +index fbdb95f0..3eb77025 100644 +--- a/adapters/mgid/usersync.go ++++ b/adapters/mgid/usersync.go +@@ -3,8 +3,8 @@ package mgid + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewMgidSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/mgid/usersync_test.go b/adapters/mgid/usersync_test.go +index 2ce634ee..b918dabf 100644 +--- a/adapters/mgid/usersync_test.go ++++ b/adapters/mgid/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/mobfoxpb/mobfoxpb.go b/adapters/mobfoxpb/mobfoxpb.go +index 5369c082..14a14af6 100644 +--- a/adapters/mobfoxpb/mobfoxpb.go ++++ b/adapters/mobfoxpb/mobfoxpb.go +@@ -6,11 +6,11 @@ import ( + "net/http" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type adapter struct { +diff --git a/adapters/mobfoxpb/mobfoxpb_test.go b/adapters/mobfoxpb/mobfoxpb_test.go +index 271d30a9..23bdb281 100644 +--- a/adapters/mobfoxpb/mobfoxpb_test.go ++++ b/adapters/mobfoxpb/mobfoxpb_test.go +@@ -3,9 +3,9 @@ package mobfoxpb + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/mobfoxpb/params_test.go b/adapters/mobfoxpb/params_test.go +index 59b9ec38..ddd738ec 100644 +--- a/adapters/mobfoxpb/params_test.go ++++ b/adapters/mobfoxpb/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the mobfoxpb schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/mobilefuse/mobilefuse.go b/adapters/mobilefuse/mobilefuse.go +index 76c18e27..4e545ec1 100644 +--- a/adapters/mobilefuse/mobilefuse.go ++++ b/adapters/mobilefuse/mobilefuse.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type MobileFuseAdapter struct { +diff --git a/adapters/mobilefuse/mobilefuse_test.go b/adapters/mobilefuse/mobilefuse_test.go +index 3abe627f..52d2ab20 100644 +--- a/adapters/mobilefuse/mobilefuse_test.go ++++ b/adapters/mobilefuse/mobilefuse_test.go +@@ -3,9 +3,9 @@ package mobilefuse + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/mobilefuse/params_test.go b/adapters/mobilefuse/params_test.go +index dbfd8894..6d98f656 100644 +--- a/adapters/mobilefuse/params_test.go ++++ b/adapters/mobilefuse/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(test *testing.T) { +diff --git a/adapters/nanointeractive/nanointeractive.go b/adapters/nanointeractive/nanointeractive.go +index 82574d8b..9dfee06e 100644 +--- a/adapters/nanointeractive/nanointeractive.go ++++ b/adapters/nanointeractive/nanointeractive.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type NanoInteractiveAdapter struct { +diff --git a/adapters/nanointeractive/nanointeractive_test.go b/adapters/nanointeractive/nanointeractive_test.go +index d0955511..79caf9cf 100644 +--- a/adapters/nanointeractive/nanointeractive_test.go ++++ b/adapters/nanointeractive/nanointeractive_test.go +@@ -3,9 +3,9 @@ package nanointeractive + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/nanointeractive/params_test.go b/adapters/nanointeractive/params_test.go +index b290f3d9..309d19b5 100644 +--- a/adapters/nanointeractive/params_test.go ++++ b/adapters/nanointeractive/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/nanointeractive.json +diff --git a/adapters/nanointeractive/usersync.go b/adapters/nanointeractive/usersync.go +index e6227436..180e2c53 100644 +--- a/adapters/nanointeractive/usersync.go ++++ b/adapters/nanointeractive/usersync.go +@@ -3,8 +3,8 @@ package nanointeractive + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewNanoInteractiveSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go +index fa786649..44040756 100644 +--- a/adapters/nanointeractive/usersync_test.go ++++ b/adapters/nanointeractive/usersync_test.go +@@ -4,10 +4,10 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/ninthdecimal/ninthdecimal.go b/adapters/ninthdecimal/ninthdecimal.go +index 03095ee6..5cb631db 100755 +--- a/adapters/ninthdecimal/ninthdecimal.go ++++ b/adapters/ninthdecimal/ninthdecimal.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type NinthDecimalAdapter struct { +diff --git a/adapters/ninthdecimal/ninthdecimal_test.go b/adapters/ninthdecimal/ninthdecimal_test.go +index ccb8114f..9d82f387 100755 +--- a/adapters/ninthdecimal/ninthdecimal_test.go ++++ b/adapters/ninthdecimal/ninthdecimal_test.go +@@ -3,9 +3,9 @@ package ninthdecimal + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/ninthdecimal/params_test.go b/adapters/ninthdecimal/params_test.go +index 8d3ef3d7..cc06088c 100755 +--- a/adapters/ninthdecimal/params_test.go ++++ b/adapters/ninthdecimal/params_test.go +@@ -2,7 +2,7 @@ package ninthdecimal + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/ninthdecimal/usersync.go b/adapters/ninthdecimal/usersync.go +index 7a8d029c..4ea91d03 100755 +--- a/adapters/ninthdecimal/usersync.go ++++ b/adapters/ninthdecimal/usersync.go +@@ -3,8 +3,8 @@ package ninthdecimal + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewNinthDecimalSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/ninthdecimal/usersync_test.go b/adapters/ninthdecimal/usersync_test.go +index ee121434..ded5bf0d 100755 +--- a/adapters/ninthdecimal/usersync_test.go ++++ b/adapters/ninthdecimal/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/nobid/nobid.go b/adapters/nobid/nobid.go +index 875a7260..77d1a7f8 100644 +--- a/adapters/nobid/nobid.go ++++ b/adapters/nobid/nobid.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // NoBidAdapter - NoBid Adapter definition +diff --git a/adapters/nobid/nobid_test.go b/adapters/nobid/nobid_test.go +index 674d189d..420c57f3 100644 +--- a/adapters/nobid/nobid_test.go ++++ b/adapters/nobid/nobid_test.go +@@ -3,9 +3,9 @@ package nobid + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/nobid/params_test.go b/adapters/nobid/params_test.go +index 75d69943..f8c0533c 100644 +--- a/adapters/nobid/params_test.go ++++ b/adapters/nobid/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/nobid/usersync.go b/adapters/nobid/usersync.go +index c21413f5..3b36e59f 100644 +--- a/adapters/nobid/usersync.go ++++ b/adapters/nobid/usersync.go +@@ -3,8 +3,8 @@ package nobid + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewNoBidSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/nobid/usersync_test.go b/adapters/nobid/usersync_test.go +index bc55d130..a59d70a4 100644 +--- a/adapters/nobid/usersync_test.go ++++ b/adapters/nobid/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/openrtb_util.go b/adapters/openrtb_util.go +index 55064ea7..88023920 100644 +--- a/adapters/openrtb_util.go ++++ b/adapters/openrtb_util.go +@@ -3,10 +3,10 @@ package adapters + import ( + "encoding/json" + +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + func min(x, y int) int { +diff --git a/adapters/openrtb_util_test.go b/adapters/openrtb_util_test.go +index 2cf67c22..fbb9ab57 100644 +--- a/adapters/openrtb_util_test.go ++++ b/adapters/openrtb_util_test.go +@@ -5,9 +5,9 @@ import ( + + "encoding/json" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/openx/openx.go b/adapters/openx/openx.go +index b6640b1f..21fbd37e 100644 +--- a/adapters/openx/openx.go ++++ b/adapters/openx/openx.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const hbconfig = "hb_pbs_1.0.0" +diff --git a/adapters/openx/openx_test.go b/adapters/openx/openx_test.go +index 81d2ff22..5eec4feb 100644 +--- a/adapters/openx/openx_test.go ++++ b/adapters/openx/openx_test.go +@@ -4,11 +4,11 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/openx/params_test.go b/adapters/openx/params_test.go +index b7ea970a..87ce08fc 100644 +--- a/adapters/openx/params_test.go ++++ b/adapters/openx/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/openx.json +diff --git a/adapters/openx/usersync.go b/adapters/openx/usersync.go +index bb4b328c..f557e5e4 100644 +--- a/adapters/openx/usersync.go ++++ b/adapters/openx/usersync.go +@@ -3,8 +3,8 @@ package openx + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewOpenxSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/openx/usersync_test.go b/adapters/openx/usersync_test.go +index cdbb7da8..7bb30399 100644 +--- a/adapters/openx/usersync_test.go ++++ b/adapters/openx/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/orbidder/orbidder.go b/adapters/orbidder/orbidder.go +index e26ad4ef..4e13fc6f 100644 +--- a/adapters/orbidder/orbidder.go ++++ b/adapters/orbidder/orbidder.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type OrbidderAdapter struct { +diff --git a/adapters/orbidder/orbidder_test.go b/adapters/orbidder/orbidder_test.go +index 0eaed23a..4e9fc2f8 100644 +--- a/adapters/orbidder/orbidder_test.go ++++ b/adapters/orbidder/orbidder_test.go +@@ -4,9 +4,9 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/orbidder/params_test.go b/adapters/orbidder/params_test.go +index 19c4ed8d..98fcf021 100644 +--- a/adapters/orbidder/params_test.go ++++ b/adapters/orbidder/params_test.go +@@ -2,7 +2,7 @@ package orbidder + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/pubmatic/params_test.go b/adapters/pubmatic/params_test.go +index c8a300b9..9615fb97 100644 +--- a/adapters/pubmatic/params_test.go ++++ b/adapters/pubmatic/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/pubmatic.json +diff --git a/adapters/pubmatic/pubmatic.go b/adapters/pubmatic/pubmatic.go +index a4c4e222..fb0fd324 100644 +--- a/adapters/pubmatic/pubmatic.go ++++ b/adapters/pubmatic/pubmatic.go +@@ -12,12 +12,12 @@ import ( + "strings" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/pubmatic/pubmatic_test.go b/adapters/pubmatic/pubmatic_test.go +index 8ed2ccd3..8c59a0a9 100644 +--- a/adapters/pubmatic/pubmatic_test.go ++++ b/adapters/pubmatic/pubmatic_test.go +@@ -12,14 +12,14 @@ import ( + "testing" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/pubmatic/usersync.go b/adapters/pubmatic/usersync.go +index 7b4d8e86..f35470c0 100644 +--- a/adapters/pubmatic/usersync.go ++++ b/adapters/pubmatic/usersync.go +@@ -3,8 +3,8 @@ package pubmatic + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewPubmaticSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/pubmatic/usersync_test.go b/adapters/pubmatic/usersync_test.go +index d6cd9f78..e32650ac 100644 +--- a/adapters/pubmatic/usersync_test.go ++++ b/adapters/pubmatic/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/pubnative/pubnative.go b/adapters/pubnative/pubnative.go +index 7d393f8a..36edceee 100644 +--- a/adapters/pubnative/pubnative.go ++++ b/adapters/pubnative/pubnative.go +@@ -7,11 +7,11 @@ import ( + "net/url" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type PubnativeAdapter struct { +diff --git a/adapters/pubnative/pubnative_test.go b/adapters/pubnative/pubnative_test.go +index 6955b85f..484315ca 100644 +--- a/adapters/pubnative/pubnative_test.go ++++ b/adapters/pubnative/pubnative_test.go +@@ -3,9 +3,9 @@ package pubnative + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/pulsepoint/params_test.go b/adapters/pulsepoint/params_test.go +index ac2b314b..f6d6baf9 100644 +--- a/adapters/pulsepoint/params_test.go ++++ b/adapters/pulsepoint/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/pulsepoint/pulsepoint.go b/adapters/pulsepoint/pulsepoint.go +index b07a2cba..8025e9b2 100644 +--- a/adapters/pulsepoint/pulsepoint.go ++++ b/adapters/pulsepoint/pulsepoint.go +@@ -11,12 +11,12 @@ import ( + "io/ioutil" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/pulsepoint/pulsepoint_test.go b/adapters/pulsepoint/pulsepoint_test.go +index 33023d05..7e9b5101 100644 +--- a/adapters/pulsepoint/pulsepoint_test.go ++++ b/adapters/pulsepoint/pulsepoint_test.go +@@ -5,9 +5,9 @@ import ( + "net/http" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "bytes" + "context" +@@ -16,11 +16,11 @@ import ( + "net/http/httptest" + "time" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/pulsepoint/usersync.go b/adapters/pulsepoint/usersync.go +index 58de835b..4c7d5ca6 100644 +--- a/adapters/pulsepoint/usersync.go ++++ b/adapters/pulsepoint/usersync.go +@@ -3,8 +3,8 @@ package pulsepoint + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewPulsepointSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/pulsepoint/usersync_test.go b/adapters/pulsepoint/usersync_test.go +index e1680fd2..8dd32564 100644 +--- a/adapters/pulsepoint/usersync_test.go ++++ b/adapters/pulsepoint/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/revcontent/revcontent.go b/adapters/revcontent/revcontent.go +index 5a34f3ef..c6a7ff9d 100644 +--- a/adapters/revcontent/revcontent.go ++++ b/adapters/revcontent/revcontent.go +@@ -3,11 +3,11 @@ package revcontent + import ( + "encoding/json" + "fmt" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "net/http" + ) + +diff --git a/adapters/revcontent/revcontent_test.go b/adapters/revcontent/revcontent_test.go +index 836ef138..d7f83cdb 100644 +--- a/adapters/revcontent/revcontent_test.go ++++ b/adapters/revcontent/revcontent_test.go +@@ -3,9 +3,9 @@ package revcontent + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/rhythmone/params_test.go b/adapters/rhythmone/params_test.go +index 7d8cad47..00eacf15 100644 +--- a/adapters/rhythmone/params_test.go ++++ b/adapters/rhythmone/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/rhythmone/rhythmone.go b/adapters/rhythmone/rhythmone.go +index 7dd94a49..e2fc9aa8 100644 +--- a/adapters/rhythmone/rhythmone.go ++++ b/adapters/rhythmone/rhythmone.go +@@ -6,11 +6,11 @@ import ( + + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type RhythmoneAdapter struct { +diff --git a/adapters/rhythmone/rhythmone_test.go b/adapters/rhythmone/rhythmone_test.go +index c0c0a891..e2419f33 100644 +--- a/adapters/rhythmone/rhythmone_test.go ++++ b/adapters/rhythmone/rhythmone_test.go +@@ -3,9 +3,9 @@ package rhythmone + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/rhythmone/usersync.go b/adapters/rhythmone/usersync.go +index 0f7db388..534c60dd 100644 +--- a/adapters/rhythmone/usersync.go ++++ b/adapters/rhythmone/usersync.go +@@ -3,8 +3,8 @@ package rhythmone + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewRhythmoneSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/rhythmone/usersync_test.go b/adapters/rhythmone/usersync_test.go +index 85ecba2a..1c725626 100644 +--- a/adapters/rhythmone/usersync_test.go ++++ b/adapters/rhythmone/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/rtbhouse/rtbhouse.go b/adapters/rtbhouse/rtbhouse.go +index 1f86d4f3..26f85d4a 100644 +--- a/adapters/rtbhouse/rtbhouse.go ++++ b/adapters/rtbhouse/rtbhouse.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Builder builds a new instance of the RTBHouse adapter for the given bidder with the given config. +diff --git a/adapters/rtbhouse/rtbhouse_test.go b/adapters/rtbhouse/rtbhouse_test.go +index bae9a636..15dfd1fc 100644 +--- a/adapters/rtbhouse/rtbhouse_test.go ++++ b/adapters/rtbhouse/rtbhouse_test.go +@@ -3,9 +3,9 @@ package rtbhouse + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const testsDir = "rtbhousetest" +diff --git a/adapters/rtbhouse/usersync.go b/adapters/rtbhouse/usersync.go +index 3e38d67e..97e67312 100644 +--- a/adapters/rtbhouse/usersync.go ++++ b/adapters/rtbhouse/usersync.go +@@ -3,8 +3,8 @@ package rtbhouse + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const rtbHouseGDPRVendorID = uint16(16) +diff --git a/adapters/rtbhouse/usersync_test.go b/adapters/rtbhouse/usersync_test.go +index bb139f7a..7b1d198b 100644 +--- a/adapters/rtbhouse/usersync_test.go ++++ b/adapters/rtbhouse/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go +index 91211a61..dfee2325 100644 +--- a/adapters/rubicon/rubicon.go ++++ b/adapters/rubicon/rubicon.go +@@ -11,15 +11,15 @@ import ( + "strings" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + + "golang.org/x/net/context/ctxhttp" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const badvLimitSize = 50 +diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go +index 41e37f41..54953b21 100644 +--- a/adapters/rubicon/rubicon_test.go ++++ b/adapters/rubicon/rubicon_test.go +@@ -4,7 +4,7 @@ import ( + "bytes" + "context" + "encoding/json" +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "io/ioutil" + "net/http" + "net/http/httptest" +@@ -12,19 +12,19 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "fmt" + + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "github.com/stretchr/testify/assert" + ) +diff --git a/adapters/rubicon/usersync.go b/adapters/rubicon/usersync.go +index 08d98825..9c860247 100644 +--- a/adapters/rubicon/usersync.go ++++ b/adapters/rubicon/usersync.go +@@ -3,8 +3,8 @@ package rubicon + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewRubiconSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/rubicon/usersync_test.go b/adapters/rubicon/usersync_test.go +index 646ebff2..2fd53d6b 100644 +--- a/adapters/rubicon/usersync_test.go ++++ b/adapters/rubicon/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go +index 36af79c4..738d382a 100644 +--- a/adapters/sharethrough/butler.go ++++ b/adapters/sharethrough/butler.go +@@ -9,11 +9,11 @@ import ( + "strconv" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + ) + + const defaultTmax = 10000 // 10 sec +diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go +index 402e8365..f6d43971 100644 +--- a/adapters/sharethrough/butler_test.go ++++ b/adapters/sharethrough/butler_test.go +@@ -9,10 +9,10 @@ import ( + "testing" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sharethrough/params_test.go b/adapters/sharethrough/params_test.go +index 416f4593..e4e659f4 100644 +--- a/adapters/sharethrough/params_test.go ++++ b/adapters/sharethrough/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go +index a6ef4736..63958d02 100644 +--- a/adapters/sharethrough/sharethrough.go ++++ b/adapters/sharethrough/sharethrough.go +@@ -5,11 +5,11 @@ import ( + "net/http" + "regexp" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const supplyId = "FGMrCMMc" +diff --git a/adapters/sharethrough/sharethrough_test.go b/adapters/sharethrough/sharethrough_test.go +index 9fca80e0..0e31a90b 100644 +--- a/adapters/sharethrough/sharethrough_test.go ++++ b/adapters/sharethrough/sharethrough_test.go +@@ -6,11 +6,11 @@ import ( + "regexp" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sharethrough/usersync.go b/adapters/sharethrough/usersync.go +index a951fcd6..7d5d6f13 100644 +--- a/adapters/sharethrough/usersync.go ++++ b/adapters/sharethrough/usersync.go +@@ -1,8 +1,8 @@ + package sharethrough + + import ( +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "text/template" + ) + +diff --git a/adapters/sharethrough/usersync_test.go b/adapters/sharethrough/usersync_test.go +index 48023295..c48a6d51 100644 +--- a/adapters/sharethrough/usersync_test.go ++++ b/adapters/sharethrough/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go +index 76d8b7a1..2421dbc9 100644 +--- a/adapters/sharethrough/utils.go ++++ b/adapters/sharethrough/utils.go +@@ -6,8 +6,8 @@ import ( + "encoding/json" + "fmt" + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "html/template" + "net" + "net/url" +diff --git a/adapters/sharethrough/utils_test.go b/adapters/sharethrough/utils_test.go +index fffc52f6..aa5ae58c 100644 +--- a/adapters/sharethrough/utils_test.go ++++ b/adapters/sharethrough/utils_test.go +@@ -4,8 +4,8 @@ import ( + "encoding/base64" + "encoding/json" + "fmt" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + "regexp" + "testing" +diff --git a/adapters/silvermob/params_test.go b/adapters/silvermob/params_test.go +index 13009f6a..fbfb5f3d 100644 +--- a/adapters/silvermob/params_test.go ++++ b/adapters/silvermob/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // TestValidParams makes sure that the silvermob schema accepts all imp.ext fields which we intend to support. +diff --git a/adapters/silvermob/silvermob.go b/adapters/silvermob/silvermob.go +index 2e31e51c..0e79f443 100644 +--- a/adapters/silvermob/silvermob.go ++++ b/adapters/silvermob/silvermob.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type SilverMobAdapter struct { +diff --git a/adapters/silvermob/silvermob_test.go b/adapters/silvermob/silvermob_test.go +index 795d58fd..8045542c 100644 +--- a/adapters/silvermob/silvermob_test.go ++++ b/adapters/silvermob/silvermob_test.go +@@ -3,9 +3,9 @@ package silvermob + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/smaato/params_test.go b/adapters/smaato/params_test.go +index 6c71cbe7..80993dc5 100644 +--- a/adapters/smaato/params_test.go ++++ b/adapters/smaato/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file intends to test static/bidder-params/smaato.json +diff --git a/adapters/smaato/smaato.go b/adapters/smaato/smaato.go +index 1d5b29ab..67e71e65 100644 +--- a/adapters/smaato/smaato.go ++++ b/adapters/smaato/smaato.go +@@ -7,11 +7,11 @@ import ( + "strings" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const clientVersion = "prebid_server_0.1" +diff --git a/adapters/smaato/smaato_test.go b/adapters/smaato/smaato_test.go +index c7c4a650..5fdea316 100644 +--- a/adapters/smaato/smaato_test.go ++++ b/adapters/smaato/smaato_test.go +@@ -3,9 +3,9 @@ package smaato + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/smartadserver/params_test.go b/adapters/smartadserver/params_test.go +index b424cbe0..363886d9 100644 +--- a/adapters/smartadserver/params_test.go ++++ b/adapters/smartadserver/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/smartadserver.json +diff --git a/adapters/smartadserver/smartadserver.go b/adapters/smartadserver/smartadserver.go +index e2735d3b..1231ab67 100644 +--- a/adapters/smartadserver/smartadserver.go ++++ b/adapters/smartadserver/smartadserver.go +@@ -8,11 +8,11 @@ import ( + "path" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type SmartAdserverAdapter struct { +diff --git a/adapters/smartadserver/smartadserver_test.go b/adapters/smartadserver/smartadserver_test.go +index 978be336..e6cf5e12 100644 +--- a/adapters/smartadserver/smartadserver_test.go ++++ b/adapters/smartadserver/smartadserver_test.go +@@ -3,9 +3,9 @@ package smartadserver + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/smartadserver/usersync.go b/adapters/smartadserver/usersync.go +index 95b305ff..cc155966 100644 +--- a/adapters/smartadserver/usersync.go ++++ b/adapters/smartadserver/usersync.go +@@ -3,8 +3,8 @@ package smartadserver + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSmartadserverSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/smartadserver/usersync_test.go b/adapters/smartadserver/usersync_test.go +index c4e66606..aeb34dd6 100644 +--- a/adapters/smartadserver/usersync_test.go ++++ b/adapters/smartadserver/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/smartrtb/smartrtb.go b/adapters/smartrtb/smartrtb.go +index 364c52a7..0613bb68 100644 +--- a/adapters/smartrtb/smartrtb.go ++++ b/adapters/smartrtb/smartrtb.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Base adapter structure. +diff --git a/adapters/smartrtb/smartrtb_test.go b/adapters/smartrtb/smartrtb_test.go +index ef5fce66..1d592dfc 100644 +--- a/adapters/smartrtb/smartrtb_test.go ++++ b/adapters/smartrtb/smartrtb_test.go +@@ -3,9 +3,9 @@ package smartrtb + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/smartrtb/usersync.go b/adapters/smartrtb/usersync.go +index 2f7b1dc3..1148dcc1 100644 +--- a/adapters/smartrtb/usersync.go ++++ b/adapters/smartrtb/usersync.go +@@ -3,8 +3,8 @@ package smartrtb + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSmartRTBSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/smartrtb/usersync_test.go b/adapters/smartrtb/usersync_test.go +index ae3ae5dc..6fa5c837 100644 +--- a/adapters/smartrtb/usersync_test.go ++++ b/adapters/smartrtb/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/smartyads/params_test.go b/adapters/smartyads/params_test.go +index 3aa5c0e8..048989a2 100644 +--- a/adapters/smartyads/params_test.go ++++ b/adapters/smartyads/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + var validParams = []string{ +diff --git a/adapters/smartyads/smartyads.go b/adapters/smartyads/smartyads.go +index ba727b47..93a94a74 100644 +--- a/adapters/smartyads/smartyads.go ++++ b/adapters/smartyads/smartyads.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type SmartyAdsAdapter struct { +diff --git a/adapters/smartyads/smartyads_test.go b/adapters/smartyads/smartyads_test.go +index 4ea20e98..dae8e2a0 100644 +--- a/adapters/smartyads/smartyads_test.go ++++ b/adapters/smartyads/smartyads_test.go +@@ -3,9 +3,9 @@ package smartyads + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/smartyads/usersync.go b/adapters/smartyads/usersync.go +index 839621e9..d394fb73 100644 +--- a/adapters/smartyads/usersync.go ++++ b/adapters/smartyads/usersync.go +@@ -3,8 +3,8 @@ package smartyads + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSmartyAdsSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/smartyads/usersync_test.go b/adapters/smartyads/usersync_test.go +index ed7fc07b..6ea8aa81 100644 +--- a/adapters/smartyads/usersync_test.go ++++ b/adapters/smartyads/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/somoaudience/params_test.go b/adapters/somoaudience/params_test.go +index 2cbb2b1f..b74725aa 100644 +--- a/adapters/somoaudience/params_test.go ++++ b/adapters/somoaudience/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/somoaudience.json +diff --git a/adapters/somoaudience/somoaudience.go b/adapters/somoaudience/somoaudience.go +index 2bcb0282..2e096dfe 100644 +--- a/adapters/somoaudience/somoaudience.go ++++ b/adapters/somoaudience/somoaudience.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "strconv" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + const hbconfig = "hb_pbs_1.0.0" +diff --git a/adapters/somoaudience/somoaudience_test.go b/adapters/somoaudience/somoaudience_test.go +index 07a1d2b3..3f3ce8c6 100644 +--- a/adapters/somoaudience/somoaudience_test.go ++++ b/adapters/somoaudience/somoaudience_test.go +@@ -3,9 +3,9 @@ package somoaudience + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/somoaudience/usersync.go b/adapters/somoaudience/usersync.go +index e1e7e2ef..2d0b715d 100644 +--- a/adapters/somoaudience/usersync.go ++++ b/adapters/somoaudience/usersync.go +@@ -3,8 +3,8 @@ package somoaudience + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSomoaudienceSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/somoaudience/usersync_test.go b/adapters/somoaudience/usersync_test.go +index 86460506..9abcc65e 100644 +--- a/adapters/somoaudience/usersync_test.go ++++ b/adapters/somoaudience/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sonobi/params_test.go b/adapters/sonobi/params_test.go +index 3e9d63e8..00fe63c6 100644 +--- a/adapters/sonobi/params_test.go ++++ b/adapters/sonobi/params_test.go +@@ -2,7 +2,7 @@ package sonobi + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/sonobi/sonobi.go b/adapters/sonobi/sonobi.go +index a13e9c67..3deebbbd 100644 +--- a/adapters/sonobi/sonobi.go ++++ b/adapters/sonobi/sonobi.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // SonobiAdapter - Sonobi SonobiAdapter definition +diff --git a/adapters/sonobi/sonobi_test.go b/adapters/sonobi/sonobi_test.go +index 7e8f2dc2..7a5d94bd 100644 +--- a/adapters/sonobi/sonobi_test.go ++++ b/adapters/sonobi/sonobi_test.go +@@ -3,9 +3,9 @@ package sonobi + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/sonobi/usersync.go b/adapters/sonobi/usersync.go +index 6986d0fd..6ac95056 100644 +--- a/adapters/sonobi/usersync.go ++++ b/adapters/sonobi/usersync.go +@@ -1,8 +1,8 @@ + package sonobi + + import ( +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "text/template" + ) + +diff --git a/adapters/sonobi/usersync_test.go b/adapters/sonobi/usersync_test.go +index bfe5859e..8eadaef8 100644 +--- a/adapters/sonobi/usersync_test.go ++++ b/adapters/sonobi/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/sovrn/sovrn.go b/adapters/sovrn/sovrn.go +index 9e904872..24920fa7 100644 +--- a/adapters/sovrn/sovrn.go ++++ b/adapters/sovrn/sovrn.go +@@ -12,12 +12,12 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/adapters/sovrn/sovrn_test.go b/adapters/sovrn/sovrn_test.go +index aa1b98f0..5f43c1a5 100644 +--- a/adapters/sovrn/sovrn_test.go ++++ b/adapters/sovrn/sovrn_test.go +@@ -8,10 +8,10 @@ import ( + "net/http/httptest" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "context" + "net/http" +@@ -19,10 +19,10 @@ import ( + "strconv" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/sovrn/usersync.go b/adapters/sovrn/usersync.go +index b4de6237..3f4e8143 100644 +--- a/adapters/sovrn/usersync.go ++++ b/adapters/sovrn/usersync.go +@@ -3,8 +3,8 @@ package sovrn + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSovrnSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/sovrn/usersync_test.go b/adapters/sovrn/usersync_test.go +index 4a4dfd9d..e91c7324 100644 +--- a/adapters/sovrn/usersync_test.go ++++ b/adapters/sovrn/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/synacormedia/params_test.go b/adapters/synacormedia/params_test.go +index a216818e..d7585c9d 100644 +--- a/adapters/synacormedia/params_test.go ++++ b/adapters/synacormedia/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/synacormedia.json +diff --git a/adapters/synacormedia/synacormedia.go b/adapters/synacormedia/synacormedia.go +index 7c9b4fab..e87c665d 100644 +--- a/adapters/synacormedia/synacormedia.go ++++ b/adapters/synacormedia/synacormedia.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type SynacorMediaAdapter struct { +diff --git a/adapters/synacormedia/synacormedia_test.go b/adapters/synacormedia/synacormedia_test.go +index 6a018dc3..ce696a67 100644 +--- a/adapters/synacormedia/synacormedia_test.go ++++ b/adapters/synacormedia/synacormedia_test.go +@@ -3,9 +3,9 @@ package synacormedia + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/synacormedia/usersync.go b/adapters/synacormedia/usersync.go +index 988c1b9e..d7de9150 100644 +--- a/adapters/synacormedia/usersync.go ++++ b/adapters/synacormedia/usersync.go +@@ -3,8 +3,8 @@ package synacormedia + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewSynacorMediaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/synacormedia/usersync_test.go b/adapters/synacormedia/usersync_test.go +index 538efb30..7ee7b1aa 100644 +--- a/adapters/synacormedia/usersync_test.go ++++ b/adapters/synacormedia/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/syncer.go b/adapters/syncer.go +index 122bcc7e..13985db6 100644 +--- a/adapters/syncer.go ++++ b/adapters/syncer.go +@@ -3,10 +3,10 @@ package adapters + import ( + "text/template" + +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func GDPRAwareSyncerIDs(syncers map[openrtb_ext.BidderName]usersync.Usersyncer) map[openrtb_ext.BidderName]uint16 { +diff --git a/adapters/syncer_test.go b/adapters/syncer_test.go +index ca33a9a1..7961608c 100644 +--- a/adapters/syncer_test.go ++++ b/adapters/syncer_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/tappx/params_test.go b/adapters/tappx/params_test.go +index 3a73d4da..d6fcbb9c 100644 +--- a/adapters/tappx/params_test.go ++++ b/adapters/tappx/params_test.go +@@ -2,7 +2,7 @@ package tappx + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/tappx/tappx.go b/adapters/tappx/tappx.go +index de8cb49a..2e044e82 100644 +--- a/adapters/tappx/tappx.go ++++ b/adapters/tappx/tappx.go +@@ -9,12 +9,12 @@ import ( + "text/template" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const TAPPX_BIDDER_VERSION = "1.1" +diff --git a/adapters/tappx/tappx_test.go b/adapters/tappx/tappx_test.go +index 5346b82b..465828b0 100644 +--- a/adapters/tappx/tappx_test.go ++++ b/adapters/tappx/tappx_test.go +@@ -4,9 +4,9 @@ import ( + "regexp" + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/telaria/params_test.go b/adapters/telaria/params_test.go +index efa3fba1..76f936ce 100644 +--- a/adapters/telaria/params_test.go ++++ b/adapters/telaria/params_test.go +@@ -2,7 +2,7 @@ package telaria + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go +index d99d4a98..57cb5392 100644 +--- a/adapters/telaria/telaria.go ++++ b/adapters/telaria/telaria.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const Endpoint = "https://ads.tremorhub.com/ad/rtb/prebid" +diff --git a/adapters/telaria/telaria_test.go b/adapters/telaria/telaria_test.go +index be29ed40..b7eddbad 100644 +--- a/adapters/telaria/telaria_test.go ++++ b/adapters/telaria/telaria_test.go +@@ -3,9 +3,9 @@ package telaria + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/telaria/usersync.go b/adapters/telaria/usersync.go +index e3f76f6e..71cf85b6 100644 +--- a/adapters/telaria/usersync.go ++++ b/adapters/telaria/usersync.go +@@ -3,8 +3,8 @@ package telaria + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewTelariaSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/telaria/usersync_test.go b/adapters/telaria/usersync_test.go +index 4896b253..dc3accbb 100644 +--- a/adapters/telaria/usersync_test.go ++++ b/adapters/telaria/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/triplelift/triplelift.go b/adapters/triplelift/triplelift.go +index 0b100094..6b9f5ecf 100644 +--- a/adapters/triplelift/triplelift.go ++++ b/adapters/triplelift/triplelift.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type TripleliftAdapter struct { +diff --git a/adapters/triplelift/triplelift_test.go b/adapters/triplelift/triplelift_test.go +index 5107d7cc..3f23e0dc 100644 +--- a/adapters/triplelift/triplelift_test.go ++++ b/adapters/triplelift/triplelift_test.go +@@ -3,9 +3,9 @@ package triplelift + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/triplelift/usersync.go b/adapters/triplelift/usersync.go +index 5cb524be..0bd678b0 100644 +--- a/adapters/triplelift/usersync.go ++++ b/adapters/triplelift/usersync.go +@@ -3,8 +3,8 @@ package triplelift + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/triplelift/usersync_test.go b/adapters/triplelift/usersync_test.go +index 1731b22d..30b1a33b 100644 +--- a/adapters/triplelift/usersync_test.go ++++ b/adapters/triplelift/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/triplelift_native/triplelift_native.go b/adapters/triplelift_native/triplelift_native.go +index 9936bf82..65c6994c 100644 +--- a/adapters/triplelift_native/triplelift_native.go ++++ b/adapters/triplelift_native/triplelift_native.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type TripleliftNativeAdapter struct { +diff --git a/adapters/triplelift_native/triplelift_native_test.go b/adapters/triplelift_native/triplelift_native_test.go +index fef24949..19b40232 100644 +--- a/adapters/triplelift_native/triplelift_native_test.go ++++ b/adapters/triplelift_native/triplelift_native_test.go +@@ -3,9 +3,9 @@ package triplelift_native + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/triplelift_native/usersync.go b/adapters/triplelift_native/usersync.go +index b7587b77..a8d246f0 100644 +--- a/adapters/triplelift_native/usersync.go ++++ b/adapters/triplelift_native/usersync.go +@@ -3,8 +3,8 @@ package triplelift_native + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewTripleliftSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/triplelift_native/usersync_test.go b/adapters/triplelift_native/usersync_test.go +index df0757ed..ec229e2e 100644 +--- a/adapters/triplelift_native/usersync_test.go ++++ b/adapters/triplelift_native/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/ucfunnel/params_test.go b/adapters/ucfunnel/params_test.go +index 4faec873..c33bc89a 100644 +--- a/adapters/ucfunnel/params_test.go ++++ b/adapters/ucfunnel/params_test.go +@@ -2,7 +2,7 @@ package ucfunnel + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/ucfunnel/ucfunnel.go b/adapters/ucfunnel/ucfunnel.go +index 8df715e3..7a6060f2 100644 +--- a/adapters/ucfunnel/ucfunnel.go ++++ b/adapters/ucfunnel/ucfunnel.go +@@ -6,11 +6,11 @@ import ( + "net/http" + "net/url" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type UcfunnelAdapter struct { +diff --git a/adapters/ucfunnel/ucfunnel_test.go b/adapters/ucfunnel/ucfunnel_test.go +index 216c06df..3860c988 100644 +--- a/adapters/ucfunnel/ucfunnel_test.go ++++ b/adapters/ucfunnel/ucfunnel_test.go +@@ -5,10 +5,10 @@ import ( + "fmt" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestMakeRequests(t *testing.T) { +diff --git a/adapters/ucfunnel/usersync.go b/adapters/ucfunnel/usersync.go +index 92eba0d7..8220fe6e 100644 +--- a/adapters/ucfunnel/usersync.go ++++ b/adapters/ucfunnel/usersync.go +@@ -3,8 +3,8 @@ package ucfunnel + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewUcfunnelSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/ucfunnel/usersync_test.go b/adapters/ucfunnel/usersync_test.go +index 45320b8c..25d879d8 100644 +--- a/adapters/ucfunnel/usersync_test.go ++++ b/adapters/ucfunnel/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/unruly/params_test.go b/adapters/unruly/params_test.go +index 9b8f3110..af1f2f2b 100644 +--- a/adapters/unruly/params_test.go ++++ b/adapters/unruly/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestValidParams(t *testing.T) { +diff --git a/adapters/unruly/unruly.go b/adapters/unruly/unruly.go +index ae0243aa..131e53af 100644 +--- a/adapters/unruly/unruly.go ++++ b/adapters/unruly/unruly.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type UnrulyAdapter struct { +diff --git a/adapters/unruly/unruly_test.go b/adapters/unruly/unruly_test.go +index 18a21b7d..e0f3ccc7 100644 +--- a/adapters/unruly/unruly_test.go ++++ b/adapters/unruly/unruly_test.go +@@ -7,12 +7,12 @@ import ( + "reflect" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/unruly/usersync.go b/adapters/unruly/usersync.go +index c90052a4..248b4923 100644 +--- a/adapters/unruly/usersync.go ++++ b/adapters/unruly/usersync.go +@@ -3,8 +3,8 @@ package unruly + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewUnrulySyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/unruly/usersync_test.go b/adapters/unruly/usersync_test.go +index 2f0e07d8..e85a066d 100644 +--- a/adapters/unruly/usersync_test.go ++++ b/adapters/unruly/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/valueimpression/params_test.go b/adapters/valueimpression/params_test.go +index 46471de2..b80962ff 100644 +--- a/adapters/valueimpression/params_test.go ++++ b/adapters/valueimpression/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/valueimpression.json +diff --git a/adapters/valueimpression/usersync.go b/adapters/valueimpression/usersync.go +index 34addbc0..4cc15391 100644 +--- a/adapters/valueimpression/usersync.go ++++ b/adapters/valueimpression/usersync.go +@@ -3,8 +3,8 @@ package valueimpression + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewValueImpressionSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/valueimpression/usersync_test.go b/adapters/valueimpression/usersync_test.go +index ffb3f372..ed75969a 100644 +--- a/adapters/valueimpression/usersync_test.go ++++ b/adapters/valueimpression/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/valueimpression/valueimpression.go b/adapters/valueimpression/valueimpression.go +index 5cbcf3e1..87b66674 100644 +--- a/adapters/valueimpression/valueimpression.go ++++ b/adapters/valueimpression/valueimpression.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ValueImpressionAdapter struct { +diff --git a/adapters/valueimpression/valueimpression_test.go b/adapters/valueimpression/valueimpression_test.go +index f4d33864..b2c65003 100644 +--- a/adapters/valueimpression/valueimpression_test.go ++++ b/adapters/valueimpression/valueimpression_test.go +@@ -3,9 +3,9 @@ package valueimpression + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/verizonmedia/params_test.go b/adapters/verizonmedia/params_test.go +index febda605..9250c265 100644 +--- a/adapters/verizonmedia/params_test.go ++++ b/adapters/verizonmedia/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/verizonmedia.json +diff --git a/adapters/verizonmedia/usersync.go b/adapters/verizonmedia/usersync.go +index 31fb12a2..612aab3b 100644 +--- a/adapters/verizonmedia/usersync.go ++++ b/adapters/verizonmedia/usersync.go +@@ -1,8 +1,8 @@ + package verizonmedia + + import ( +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "text/template" + ) + +diff --git a/adapters/verizonmedia/usersync_test.go b/adapters/verizonmedia/usersync_test.go +index ad77ef0e..6455078a 100644 +--- a/adapters/verizonmedia/usersync_test.go ++++ b/adapters/verizonmedia/usersync_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/verizonmedia/verizonmedia.go b/adapters/verizonmedia/verizonmedia.go +index a721b67c..d90deea3 100644 +--- a/adapters/verizonmedia/verizonmedia.go ++++ b/adapters/verizonmedia/verizonmedia.go +@@ -6,11 +6,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type VerizonMediaAdapter struct { +diff --git a/adapters/verizonmedia/verizonmedia_test.go b/adapters/verizonmedia/verizonmedia_test.go +index 869ae9e9..38bb41ec 100644 +--- a/adapters/verizonmedia/verizonmedia_test.go ++++ b/adapters/verizonmedia/verizonmedia_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + + "github.com/influxdata/influxdb/pkg/testing/assert" +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestVerizonMediaBidderEndpointConfig(t *testing.T) { +diff --git a/adapters/visx/params_test.go b/adapters/visx/params_test.go +index e857e8d2..f59ce49a 100644 +--- a/adapters/visx/params_test.go ++++ b/adapters/visx/params_test.go +@@ -2,7 +2,7 @@ package visx + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/visx/usersync.go b/adapters/visx/usersync.go +index fe1f5a42..e8223083 100644 +--- a/adapters/visx/usersync.go ++++ b/adapters/visx/usersync.go +@@ -3,8 +3,8 @@ package visx + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewVisxSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/visx/usersync_test.go b/adapters/visx/usersync_test.go +index b410cda6..e71ee35f 100644 +--- a/adapters/visx/usersync_test.go ++++ b/adapters/visx/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/visx/visx.go b/adapters/visx/visx.go +index f5a21f22..2a00b2de 100644 +--- a/adapters/visx/visx.go ++++ b/adapters/visx/visx.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type VisxAdapter struct { +diff --git a/adapters/visx/visx_test.go b/adapters/visx/visx_test.go +index 2f51af76..d637e908 100644 +--- a/adapters/visx/visx_test.go ++++ b/adapters/visx/visx_test.go +@@ -3,9 +3,9 @@ package visx + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/vrtcal/params_test.go b/adapters/vrtcal/params_test.go +index ba57b6d8..5f80812f 100644 +--- a/adapters/vrtcal/params_test.go ++++ b/adapters/vrtcal/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + //Vrtcal doesn't currently require any custom fields. This file is included for conformity only +diff --git a/adapters/vrtcal/usersync.go b/adapters/vrtcal/usersync.go +index 04e52955..0fd97875 100644 +--- a/adapters/vrtcal/usersync.go ++++ b/adapters/vrtcal/usersync.go +@@ -3,8 +3,8 @@ package vrtcal + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewVrtcalSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/vrtcal/usersync_test.go b/adapters/vrtcal/usersync_test.go +index 26e5838d..faea653f 100644 +--- a/adapters/vrtcal/usersync_test.go ++++ b/adapters/vrtcal/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/vrtcal/vrtcal.go b/adapters/vrtcal/vrtcal.go +index dadb0323..5d4bd705 100644 +--- a/adapters/vrtcal/vrtcal.go ++++ b/adapters/vrtcal/vrtcal.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type VrtcalAdapter struct { +diff --git a/adapters/vrtcal/vrtcal_test.go b/adapters/vrtcal/vrtcal_test.go +index 33221765..c1f3cfb0 100644 +--- a/adapters/vrtcal/vrtcal_test.go ++++ b/adapters/vrtcal/vrtcal_test.go +@@ -3,9 +3,9 @@ package vrtcal + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/yeahmobi/params_test.go b/adapters/yeahmobi/params_test.go +index 997bf93a..79b8273a 100644 +--- a/adapters/yeahmobi/params_test.go ++++ b/adapters/yeahmobi/params_test.go +@@ -2,7 +2,7 @@ package yeahmobi + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/yeahmobi/yeahmobi.go b/adapters/yeahmobi/yeahmobi.go +index c62f5bc0..5132a566 100644 +--- a/adapters/yeahmobi/yeahmobi.go ++++ b/adapters/yeahmobi/yeahmobi.go +@@ -8,12 +8,12 @@ import ( + "text/template" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type YeahmobiAdapter struct { +diff --git a/adapters/yeahmobi/yeahmobi_test.go b/adapters/yeahmobi/yeahmobi_test.go +index a38480b0..9f983633 100644 +--- a/adapters/yeahmobi/yeahmobi_test.go ++++ b/adapters/yeahmobi/yeahmobi_test.go +@@ -3,9 +3,9 @@ package yeahmobi + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go +index 8c230c15..f66121e3 100644 +--- a/adapters/yieldlab/params_test.go ++++ b/adapters/yieldlab/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/yieldlab.json +diff --git a/adapters/yieldlab/usersync.go b/adapters/yieldlab/usersync.go +index 3ee9a3fd..a0462e19 100644 +--- a/adapters/yieldlab/usersync.go ++++ b/adapters/yieldlab/usersync.go +@@ -3,8 +3,8 @@ package yieldlab + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewYieldlabSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/yieldlab/usersync_test.go b/adapters/yieldlab/usersync_test.go +index 3892c16b..cdca7f9f 100644 +--- a/adapters/yieldlab/usersync_test.go ++++ b/adapters/yieldlab/usersync_test.go +@@ -6,8 +6,8 @@ import ( + + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + ) + + func TestYieldlabSyncer(t *testing.T) { +diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go +index 086ce059..adce598d 100644 +--- a/adapters/yieldlab/yieldlab.go ++++ b/adapters/yieldlab/yieldlab.go +@@ -9,13 +9,13 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "golang.org/x/text/currency" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // YieldlabAdapter connects the Yieldlab API to prebid server +diff --git a/adapters/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go +index 273117e3..83f7d1e8 100644 +--- a/adapters/yieldlab/yieldlab_test.go ++++ b/adapters/yieldlab/yieldlab_test.go +@@ -5,9 +5,9 @@ import ( + + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const testURL = "https://ad.yieldlab.net/testing/" +diff --git a/adapters/yieldmo/params_test.go b/adapters/yieldmo/params_test.go +index 0a8fe2d1..87044a2d 100644 +--- a/adapters/yieldmo/params_test.go ++++ b/adapters/yieldmo/params_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file actually intends to test static/bidder-params/yieldmo.json +diff --git a/adapters/yieldmo/usersync.go b/adapters/yieldmo/usersync.go +index 041e7e8f..25d65f22 100644 +--- a/adapters/yieldmo/usersync.go ++++ b/adapters/yieldmo/usersync.go +@@ -3,8 +3,8 @@ package yieldmo + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewYieldmoSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/yieldmo/usersync_test.go b/adapters/yieldmo/usersync_test.go +index 598710ec..1212efdb 100644 +--- a/adapters/yieldmo/usersync_test.go ++++ b/adapters/yieldmo/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/yieldmo/yieldmo.go b/adapters/yieldmo/yieldmo.go +index 2d844444..95fbfa33 100644 +--- a/adapters/yieldmo/yieldmo.go ++++ b/adapters/yieldmo/yieldmo.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type YieldmoAdapter struct { +diff --git a/adapters/yieldmo/yieldmo_test.go b/adapters/yieldmo/yieldmo_test.go +index cb0a8d60..faee55c3 100644 +--- a/adapters/yieldmo/yieldmo_test.go ++++ b/adapters/yieldmo/yieldmo_test.go +@@ -3,9 +3,9 @@ package yieldmo + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/yieldone/params_test.go b/adapters/yieldone/params_test.go +index 6048ea5d..e0142334 100644 +--- a/adapters/yieldone/params_test.go ++++ b/adapters/yieldone/params_test.go +@@ -2,7 +2,7 @@ package yieldone + + import ( + "encoding/json" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "testing" + ) + +diff --git a/adapters/yieldone/usersync.go b/adapters/yieldone/usersync.go +index bc9d1b32..333550aa 100644 +--- a/adapters/yieldone/usersync.go ++++ b/adapters/yieldone/usersync.go +@@ -3,8 +3,8 @@ package yieldone + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewYieldoneSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/yieldone/usersync_test.go b/adapters/yieldone/usersync_test.go +index 902f3b66..730f9103 100644 +--- a/adapters/yieldone/usersync_test.go ++++ b/adapters/yieldone/usersync_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/yieldone/yieldone.go b/adapters/yieldone/yieldone.go +index 5d7edd63..33debb26 100644 +--- a/adapters/yieldone/yieldone.go ++++ b/adapters/yieldone/yieldone.go +@@ -5,11 +5,11 @@ import ( + "fmt" + "net/http" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type YieldoneAdapter struct { +diff --git a/adapters/yieldone/yieldone_test.go b/adapters/yieldone/yieldone_test.go +index 8544c21f..ca2c3851 100644 +--- a/adapters/yieldone/yieldone_test.go ++++ b/adapters/yieldone/yieldone_test.go +@@ -3,9 +3,9 @@ package yieldone + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestJsonSamples(t *testing.T) { +diff --git a/adapters/zeroclickfraud/usersync.go b/adapters/zeroclickfraud/usersync.go +index 833524e4..a5435335 100644 +--- a/adapters/zeroclickfraud/usersync.go ++++ b/adapters/zeroclickfraud/usersync.go +@@ -3,8 +3,8 @@ package zeroclickfraud + import ( + "text/template" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewZeroClickFraudSyncer(temp *template.Template) usersync.Usersyncer { +diff --git a/adapters/zeroclickfraud/usersync_test.go b/adapters/zeroclickfraud/usersync_test.go +index 5e8f8fdf..445b07ab 100644 +--- a/adapters/zeroclickfraud/usersync_test.go ++++ b/adapters/zeroclickfraud/usersync_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "text/template" + +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/adapters/zeroclickfraud/zeroclickfraud.go b/adapters/zeroclickfraud/zeroclickfraud.go +index 17fbba74..eb98aed1 100644 +--- a/adapters/zeroclickfraud/zeroclickfraud.go ++++ b/adapters/zeroclickfraud/zeroclickfraud.go +@@ -7,12 +7,12 @@ import ( + "strconv" + "text/template" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/macros" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type ZeroClickFraudAdapter struct { +diff --git a/adapters/zeroclickfraud/zeroclickfraud_test.go b/adapters/zeroclickfraud/zeroclickfraud_test.go +index 654d9450..2942c25a 100644 +--- a/adapters/zeroclickfraud/zeroclickfraud_test.go ++++ b/adapters/zeroclickfraud/zeroclickfraud_test.go +@@ -3,9 +3,9 @@ package zeroclickfraud + import ( + "testing" + +- "github.com/prebid/prebid-server/adapters/adapterstest" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adapterstest" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/amp/parse.go b/amp/parse.go +index 9e0e019f..c3606e83 100644 +--- a/amp/parse.go ++++ b/amp/parse.go +@@ -6,7 +6,7 @@ import ( + "strconv" + "strings" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // Params defines the paramters of an AMP request. +diff --git a/amp/parse_test.go b/amp/parse_test.go +index e2c82d71..1b9d3080 100644 +--- a/amp/parse_test.go ++++ b/amp/parse_test.go +@@ -4,7 +4,7 @@ import ( + "net/http" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/analytics/clients/http.go b/analytics/clients/http.go +index bc7f863e..68af3073 100644 +--- a/analytics/clients/http.go ++++ b/analytics/clients/http.go +@@ -7,6 +7,6 @@ import ( + var defaultHttpInstance = http.DefaultClient + + func GetDefaultHttpInstance() *http.Client { +- // TODO 2020-06-22 @see https://github.com/prebid/prebid-server/pull/1331#discussion_r436110097 ++ // TODO 2020-06-22 @see https://github.com/PubMatic-OpenWrap/prebid-server/pull/1331#discussion_r436110097 + return defaultHttpInstance + } +diff --git a/analytics/config/config.go b/analytics/config/config.go +index fa63cb5a..ff38c811 100644 +--- a/analytics/config/config.go ++++ b/analytics/config/config.go +@@ -2,11 +2,11 @@ package config + + import ( + "github.com/golang/glog" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/analytics/clients" +- "github.com/prebid/prebid-server/analytics/filesystem" +- "github.com/prebid/prebid-server/analytics/pubstack" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics/clients" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics/filesystem" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + //Modules that need to be logged to need to be initialized here +diff --git a/analytics/config/config_test.go b/analytics/config/config_test.go +index 52edc79c..02ea6a54 100644 +--- a/analytics/config/config_test.go ++++ b/analytics/config/config_test.go +@@ -6,9 +6,9 @@ import ( + "os" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + const TEST_DIR string = "testFiles" +diff --git a/analytics/core.go b/analytics/core.go +index 68373045..78ca1df2 100644 +--- a/analytics/core.go ++++ b/analytics/core.go +@@ -3,10 +3,10 @@ package analytics + import ( + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + /* +diff --git a/analytics/filesystem/file_module.go b/analytics/filesystem/file_module.go +index a0721d98..e14d0d84 100644 +--- a/analytics/filesystem/file_module.go ++++ b/analytics/filesystem/file_module.go +@@ -6,7 +6,7 @@ import ( + "fmt" + + "github.com/chasex/glog" +- "github.com/prebid/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" + ) + + type RequestType string +diff --git a/analytics/filesystem/file_module_test.go b/analytics/filesystem/file_module_test.go +index cfc923c7..e46e9259 100644 +--- a/analytics/filesystem/file_module_test.go ++++ b/analytics/filesystem/file_module_test.go +@@ -1,15 +1,15 @@ + package filesystem + + import ( +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + "net/http" + "os" + "strings" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const TEST_DIR string = "testFiles" +diff --git a/analytics/pubstack/helpers/json.go b/analytics/pubstack/helpers/json.go +index f02f1120..11c5bf8e 100644 +--- a/analytics/pubstack/helpers/json.go ++++ b/analytics/pubstack/helpers/json.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "fmt" + +- "github.com/prebid/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" + ) + + func JsonifyAuctionObject(ao *analytics.AuctionObject, scope string) ([]byte, error) { +diff --git a/analytics/pubstack/helpers/json_test.go b/analytics/pubstack/helpers/json_test.go +index 4e36e8db..4f453ccd 100644 +--- a/analytics/pubstack/helpers/json_test.go ++++ b/analytics/pubstack/helpers/json_test.go +@@ -1,9 +1,9 @@ + package helpers + + import ( +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "net/http" + "testing" + ) +diff --git a/analytics/pubstack/pubstack_module.go b/analytics/pubstack/pubstack_module.go +index 60ccde02..2754817e 100644 +--- a/analytics/pubstack/pubstack_module.go ++++ b/analytics/pubstack/pubstack_module.go +@@ -2,7 +2,7 @@ package pubstack + + import ( + "fmt" +- "github.com/prebid/prebid-server/analytics/pubstack/eventchannel" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack/eventchannel" + "net/http" + "net/url" + "os" +@@ -12,9 +12,9 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/analytics/pubstack/helpers" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics/pubstack/helpers" + +- "github.com/prebid/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" + ) + + type Configuration struct { +diff --git a/analytics/pubstack/pubstack_module_test.go b/analytics/pubstack/pubstack_module_test.go +index 6bebc937..143fb217 100644 +--- a/analytics/pubstack/pubstack_module_test.go ++++ b/analytics/pubstack/pubstack_module_test.go +@@ -9,8 +9,8 @@ import ( + "testing" + "time" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/stretchr/testify/assert" + ) + +diff --git a/cache/dummycache/dummycache.go b/cache/dummycache/dummycache.go +index 8cf9f2c4..245b3632 100644 +--- a/cache/dummycache/dummycache.go ++++ b/cache/dummycache/dummycache.go +@@ -3,7 +3,7 @@ package dummycache + import ( + "fmt" + +- "github.com/prebid/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" + ) + + // Cache dummy config that will echo back results +diff --git a/cache/filecache/filecache.go b/cache/filecache/filecache.go +index 0c1ba435..b6cebbcf 100644 +--- a/cache/filecache/filecache.go ++++ b/cache/filecache/filecache.go +@@ -5,7 +5,7 @@ import ( + "io/ioutil" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" + "gopkg.in/yaml.v2" + ) + +diff --git a/cache/postgrescache/postgrescache.go b/cache/postgrescache/postgrescache.go +index df8b8fe4..c6a53552 100644 +--- a/cache/postgrescache/postgrescache.go ++++ b/cache/postgrescache/postgrescache.go +@@ -7,11 +7,11 @@ import ( + "encoding/gob" + "time" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/coocood/freecache" + "github.com/lib/pq" +- "github.com/prebid/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" + ) + + type CacheConfig struct { +diff --git a/config/adapter.go b/config/adapter.go +index ff262b18..9f3a681c 100644 +--- a/config/adapter.go ++++ b/config/adapter.go +@@ -5,7 +5,7 @@ import ( + "text/template" + + validator "github.com/asaskevich/govalidator" +- "github.com/prebid/prebid-server/macros" ++ "github.com/PubMatic-OpenWrap/prebid-server/macros" + ) + + type Adapter struct { +diff --git a/config/config.go b/config/config.go +index ae1f62e9..e4da91e5 100755 +--- a/config/config.go ++++ b/config/config.go +@@ -10,8 +10,8 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/spf13/viper" + ) + +@@ -790,7 +790,7 @@ func SetupViper(v *viper.Viper, filename string) { + } + + // Disabling adapters by default that require some specific config params. +- // If you're using one of these, make sure you check out the documentation (https://github.com/prebid/prebid-server/tree/master/docs/bidders) ++ // If you're using one of these, make sure you check out the documentation (https://github.com/PubMatic-OpenWrap/prebid-server/tree/master/docs/bidders) + // for them and specify all the parameters they need for them to work correctly. + v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb") + v.SetDefault("adapters.33across.partner_id", "") +diff --git a/config/config_test.go b/config/config_test.go +index 7ff5f0fa..f6792cb7 100644 +--- a/config/config_test.go ++++ b/config/config_test.go +@@ -11,7 +11,7 @@ import ( + + "encoding/json" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + ) +diff --git a/currency/rate_converter.go b/currency/rate_converter.go +index 39b9cd59..a2728ae2 100644 +--- a/currency/rate_converter.go ++++ b/currency/rate_converter.go +@@ -9,8 +9,8 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/util/timeutil" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/timeutil" + ) + + // RateConverter holds the currencies conversion rates dictionary +diff --git a/currency/rate_converter_test.go b/currency/rate_converter_test.go +index e1d6741d..b3e26c86 100644 +--- a/currency/rate_converter_test.go ++++ b/currency/rate_converter_test.go +@@ -9,7 +9,7 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/util/task" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/task" + "github.com/stretchr/testify/assert" + ) + +diff --git a/docs/developers/automated-tests.md b/docs/developers/automated-tests.md +index 0dff9b04..93bd28f6 100644 +--- a/docs/developers/automated-tests.md ++++ b/docs/developers/automated-tests.md +@@ -15,7 +15,7 @@ For more info on how to write tests in Go, see [the Go docs](https://golang.org/ + ## Adapter Tests + + If your adapter makes HTTP calls using standard JSON, you should use the +-[RunJSONBidderTest](https://github.com/prebid/prebid-server/blob/master/adapters/adapterstest/test_json.go#L50) function. ++[RunJSONBidderTest](https://github.com/PubMatic-OpenWrap/prebid-server/blob/master/adapters/adapterstest/test_json.go#L50) function. + + This will be much more thorough, convenient, maintainable, and reusable than writing standard Go tests + for your adapter. +diff --git a/docs/developers/code-reviews.md b/docs/developers/code-reviews.md +index d8ee820c..20c824b3 100644 +--- a/docs/developers/code-reviews.md ++++ b/docs/developers/code-reviews.md +@@ -1,7 +1,7 @@ + # Code Reviews + + ## Standards +-Anyone is free to review and comment on any [open pull requests](https://github.com/prebid/prebid-server/pulls). ++Anyone is free to review and comment on any [open pull requests](https://github.com/PubMatic-OpenWrap/prebid-server/pulls). + + All pull requests must be reviewed and approved by at least one [core member](https://github.com/orgs/prebid/teams/core/members) before merge. + +@@ -38,7 +38,7 @@ Some examples include: + - Can we improve the user's experience in any way? + - Have the relevant [docs](..) been added or updated? If not, add the `needs docs` label. + - Do you believe that the code works by looking at the unit tests? If not, suggest more tests until you do! +-- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/prebid/prebid-server/issues) explaining it. Are there better ways to achieve those goals? ++- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/PubMatic-OpenWrap/prebid-server/issues) explaining it. Are there better ways to achieve those goals? + - Does the code use any global, mutable state? [Inject dependencies](https://en.wikipedia.org/wiki/Dependency_injection) instead! + - Can the code be organized into smaller, more modular pieces? + - Is there dead code which can be deleted? Or TODO comments which should be resolved? +diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md +index 2dafa67f..b418efa2 100644 +--- a/docs/developers/contributing.md ++++ b/docs/developers/contributing.md +@@ -2,7 +2,7 @@ + + ## Create an issue + +-[Create an issue](https://github.com/prebid/prebid-server/issues/new) describing the motivation for your changes. ++[Create an issue](https://github.com/PubMatic-OpenWrap/prebid-server/issues/new) describing the motivation for your changes. + Are you fixing a bug? Improving documentation? Optimizing some slow code? + + Pull Requests without associated Issues may still be accepted, if the motivation is obvious. +@@ -38,7 +38,7 @@ those updates must be submitted in the same Pull Request as the code changes. + ## Open a Pull Request + + When you're ready, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request/) +-against the `master` branch of [our GitHub repository](https://github.com/prebid/prebid-server/compare). ++against the `master` branch of [our GitHub repository](https://github.com/PubMatic-OpenWrap/prebid-server/compare). + + Pull Requests will be vetted through [Travis CI](https://travis-ci.com/). + To reproduce these same tests locally, do: +@@ -49,5 +49,5 @@ To reproduce these same tests locally, do: + + If the tests pass locally, but fail on your PR, [update your fork](https://help.github.com/articles/syncing-a-fork/) with the latest code from `master`. + +-**Note**: We also have some [known intermittent failures](https://github.com/prebid/prebid-server/issues/103). ++**Note**: We also have some [known intermittent failures](https://github.com/PubMatic-OpenWrap/prebid-server/issues/103). + If the tests still fail after pulling `master`, don't worry about it. We'll re-run them when we review your PR. +diff --git a/endpoints/auction.go b/endpoints/auction.go +index 92e9769d..49d25f1a 100644 +--- a/endpoints/auction.go ++++ b/endpoints/auction.go +@@ -12,19 +12,19 @@ import ( + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/cache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- pbc "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/privacy" +- gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ gdprPrivacy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + type bidResult struct { +diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go +index ed9a526d..331ddc25 100644 +--- a/endpoints/auction_test.go ++++ b/endpoints/auction_test.go +@@ -10,17 +10,17 @@ import ( + "net/http/httptest" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- metricsConf "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/prebid_cache_client" +- gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr" +- "github.com/prebid/prebid-server/usersync/usersyncers" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ gdprPolicy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync/usersyncers" + "github.com/spf13/viper" + + "github.com/stretchr/testify/assert" +diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go +index bf3935f0..a3d60e5c 100644 +--- a/endpoints/cookie_sync.go ++++ b/endpoints/cookie_sync.go +@@ -13,15 +13,15 @@ import ( + "github.com/buger/jsonparser" + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ gdprPrivacy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func NewCookieSyncEndpoint( +diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go +index a6352387..b6f17e33 100644 +--- a/endpoints/cookie_sync_test.go ++++ b/endpoints/cookie_sync_test.go +@@ -11,16 +11,16 @@ import ( + + "github.com/buger/jsonparser" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/adapters/appnexus" +- "github.com/prebid/prebid-server/adapters/audienceNetwork" +- "github.com/prebid/prebid-server/adapters/lifestreet" +- "github.com/prebid/prebid-server/adapters/pubmatic" +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- metricsConf "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/currency_rates.go b/endpoints/currency_rates.go +index d35cb74c..fbb752ae 100644 +--- a/endpoints/currency_rates.go ++++ b/endpoints/currency_rates.go +@@ -6,7 +6,7 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" + ) + + // currencyRatesInfo holds currency rates information. +diff --git a/endpoints/currency_rates_test.go b/endpoints/currency_rates_test.go +index 7fc513e7..e73893aa 100644 +--- a/endpoints/currency_rates_test.go ++++ b/endpoints/currency_rates_test.go +@@ -7,7 +7,7 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/events/account_test.go b/endpoints/events/account_test.go +index ff0dd51b..3890d12f 100644 +--- a/endpoints/events/account_test.go ++++ b/endpoints/events/account_test.go +@@ -4,9 +4,9 @@ import ( + "errors" + "fmt" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http" +diff --git a/endpoints/events/event.go b/endpoints/events/event.go +index fe178d8f..e6b296c9 100644 +--- a/endpoints/events/event.go ++++ b/endpoints/events/event.go +@@ -5,11 +5,11 @@ import ( + "errors" + "fmt" + "github.com/julienschmidt/httprouter" +- accountService "github.com/prebid/prebid-server/account" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/stored_requests" ++ accountService "github.com/PubMatic-OpenWrap/prebid-server/account" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "net/http" + "net/url" + "strconv" +diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go +index ba807184..d32d01ad 100644 +--- a/endpoints/events/event_test.go ++++ b/endpoints/events/event_test.go +@@ -4,9 +4,9 @@ import ( + "context" + "encoding/base64" + "encoding/json" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http" +diff --git a/endpoints/events/vtrack.go b/endpoints/events/vtrack.go +index 9a4d9c08..aaf7d563 100644 +--- a/endpoints/events/vtrack.go ++++ b/endpoints/events/vtrack.go +@@ -12,13 +12,13 @@ import ( + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- accountService "github.com/prebid/prebid-server/account" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/stored_requests" ++ accountService "github.com/PubMatic-OpenWrap/prebid-server/account" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + const ( +diff --git a/endpoints/events/vtrack_test.go b/endpoints/events/vtrack_test.go +index 0ec04663..52665e77 100644 +--- a/endpoints/events/vtrack_test.go ++++ b/endpoints/events/vtrack_test.go +@@ -5,10 +5,10 @@ import ( + "context" + "encoding/json" + "fmt" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http/httptest" +diff --git a/endpoints/getuids.go b/endpoints/getuids.go +index 859c0e72..af431912 100644 +--- a/endpoints/getuids.go ++++ b/endpoints/getuids.go +@@ -4,8 +4,8 @@ import ( + "net/http" + + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + + "encoding/json" + ) +diff --git a/endpoints/getuids_test.go b/endpoints/getuids_test.go +index 7988acba..fb984e15 100644 +--- a/endpoints/getuids_test.go ++++ b/endpoints/getuids_test.go +@@ -5,7 +5,7 @@ import ( + "net/http/httptest" + "testing" + +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/info/bidders.go b/endpoints/info/bidders.go +index 44482106..dedcf91a 100644 +--- a/endpoints/info/bidders.go ++++ b/endpoints/info/bidders.go +@@ -7,8 +7,8 @@ import ( + "github.com/buger/jsonparser" + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // NewBiddersEndpoint implements /info/bidders +diff --git a/endpoints/info/bidders_test.go b/endpoints/info/bidders_test.go +index 8229472b..15e5f77d 100644 +--- a/endpoints/info/bidders_test.go ++++ b/endpoints/info/bidders_test.go +@@ -14,10 +14,10 @@ import ( + "github.com/julienschmidt/httprouter" + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/endpoints/info" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints/info" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + yaml "gopkg.in/yaml.v2" + ) + +diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go +index dc66163a..b335b036 100644 +--- a/endpoints/openrtb2/amp_auction.go ++++ b/endpoints/openrtb2/amp_auction.go +@@ -14,22 +14,22 @@ import ( + "github.com/buger/jsonparser" + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/mxmCherry/openrtb" +- accountService "github.com/prebid/prebid-server/account" +- "github.com/prebid/prebid-server/amp" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- "github.com/prebid/prebid-server/usersync" +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ accountService "github.com/PubMatic-OpenWrap/prebid-server/account" ++ "github.com/PubMatic-OpenWrap/prebid-server/amp" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + ) + + const defaultAmpRequestTimeoutMillis = 900 +diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go +index 97e99c6b..cbff3241 100644 +--- a/endpoints/openrtb2/amp_auction_test.go ++++ b/endpoints/openrtb2/amp_auction_test.go +@@ -11,15 +11,15 @@ import ( + "strconv" + "testing" + +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- +- "github.com/mxmCherry/openrtb" +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ ++ "github.com/PubMatic-OpenWrap/openrtb" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + gometrics "github.com/rcrowley/go-metrics" + "github.com/stretchr/testify/assert" + ) +diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go +index 04e0c2cf..2958eb61 100644 +--- a/endpoints/openrtb2/auction.go ++++ b/endpoints/openrtb2/auction.go +@@ -18,23 +18,23 @@ import ( + "github.com/gofrs/uuid" + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/mxmCherry/openrtb" +- "github.com/mxmCherry/openrtb/native" +- nativeRequests "github.com/mxmCherry/openrtb/native/request" +- accountService "github.com/prebid/prebid-server/account" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- "github.com/prebid/prebid-server/usersync" +- "github.com/prebid/prebid-server/util/httputil" +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb/native" ++ nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" ++ accountService "github.com/PubMatic-OpenWrap/prebid-server/account" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/httputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + "golang.org/x/net/publicsuffix" + ) + +@@ -831,7 +831,7 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st + // NOTE: This is not part of the official API yet, so we are not expecting clients + // to migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER} + // at this time +- // https://github.com/prebid/prebid-server/pull/846#issuecomment-476352224 ++ // https://github.com/PubMatic-OpenWrap/prebid-server/pull/846#issuecomment-476352224 + if rawPrebidExt, ok := bidderExts[openrtb_ext.PrebidExtKey]; ok { + var prebidExt openrtb_ext.ExtImpPrebid + if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil { +diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go +index b42e45b6..89965db0 100644 +--- a/endpoints/openrtb2/auction_benchmark_test.go ++++ b/endpoints/openrtb2/auction_benchmark_test.go +@@ -7,15 +7,15 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" + +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + ) + + // dummyServer returns the header bidding test ad. This response was scraped from a real appnexus server response. +diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go +index 413d7995..ee5043c1 100644 +--- a/endpoints/openrtb2/auction_test.go ++++ b/endpoints/openrtb2/auction_test.go +@@ -17,20 +17,20 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/buger/jsonparser" + jsonpatch "github.com/evanphx/json-patch" +- "github.com/mxmCherry/openrtb" +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/openrtb2/interstitial.go b/endpoints/openrtb2/interstitial.go +index b4294548..2f9c48fc 100644 +--- a/endpoints/openrtb2/interstitial.go ++++ b/endpoints/openrtb2/interstitial.go +@@ -4,10 +4,10 @@ import ( + "encoding/json" + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func processInterstitials(req *openrtb.BidRequest) error { +diff --git a/endpoints/openrtb2/interstitial_test.go b/endpoints/openrtb2/interstitial_test.go +index 1c6eb255..93d31052 100644 +--- a/endpoints/openrtb2/interstitial_test.go ++++ b/endpoints/openrtb2/interstitial_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go +index 7735d886..b0594f7c 100644 +--- a/endpoints/openrtb2/video_auction.go ++++ b/endpoints/openrtb2/video_auction.go +@@ -17,21 +17,21 @@ import ( + "github.com/buger/jsonparser" + jsonpatch "github.com/evanphx/json-patch" + "github.com/gofrs/uuid" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/mxmCherry/openrtb" +- accountService "github.com/prebid/prebid-server/account" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ accountService "github.com/PubMatic-OpenWrap/prebid-server/account" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + var defaultRequestTimeout int64 = 5000 +diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go +index a70d45ac..9bcf6522 100644 +--- a/endpoints/openrtb2/video_auction_test.go ++++ b/endpoints/openrtb2/video_auction_test.go +@@ -12,15 +12,15 @@ import ( + "strings" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/analytics" +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/stretchr/testify/assert" + ) + +diff --git a/endpoints/setuid.go b/endpoints/setuid.go +index 4bff02ac..b910b3aa 100644 +--- a/endpoints/setuid.go ++++ b/endpoints/setuid.go +@@ -10,12 +10,12 @@ import ( + "time" + + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + const ( +diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go +index fc98608e..2b46fdde 100644 +--- a/endpoints/setuid_test.go ++++ b/endpoints/setuid_test.go +@@ -10,17 +10,17 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/stretchr/testify/assert" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- metricsConf "github.com/prebid/prebid-server/metrics/config" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + ) + + func TestSetUIDEndpoint(t *testing.T) { +diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go +index f05a4d81..2bc3a99a 100755 +--- a/exchange/adapter_builders.go ++++ b/exchange/adapter_builders.go +@@ -1,102 +1,102 @@ + package exchange + + import ( +- "github.com/prebid/prebid-server/adapters" +- ttx "github.com/prebid/prebid-server/adapters/33across" +- "github.com/prebid/prebid-server/adapters/acuityads" +- "github.com/prebid/prebid-server/adapters/adform" +- "github.com/prebid/prebid-server/adapters/adgeneration" +- "github.com/prebid/prebid-server/adapters/adhese" +- "github.com/prebid/prebid-server/adapters/adkernel" +- "github.com/prebid/prebid-server/adapters/adkernelAdn" +- "github.com/prebid/prebid-server/adapters/adman" +- "github.com/prebid/prebid-server/adapters/admixer" +- "github.com/prebid/prebid-server/adapters/adocean" +- "github.com/prebid/prebid-server/adapters/adoppler" +- "github.com/prebid/prebid-server/adapters/adot" +- "github.com/prebid/prebid-server/adapters/adpone" +- "github.com/prebid/prebid-server/adapters/adprime" +- "github.com/prebid/prebid-server/adapters/adtarget" +- "github.com/prebid/prebid-server/adapters/adtelligent" +- "github.com/prebid/prebid-server/adapters/advangelists" +- "github.com/prebid/prebid-server/adapters/aja" +- "github.com/prebid/prebid-server/adapters/amx" +- "github.com/prebid/prebid-server/adapters/applogy" +- "github.com/prebid/prebid-server/adapters/appnexus" +- "github.com/prebid/prebid-server/adapters/audienceNetwork" +- "github.com/prebid/prebid-server/adapters/avocet" +- "github.com/prebid/prebid-server/adapters/beachfront" +- "github.com/prebid/prebid-server/adapters/beintoo" +- "github.com/prebid/prebid-server/adapters/between" +- "github.com/prebid/prebid-server/adapters/brightroll" +- "github.com/prebid/prebid-server/adapters/colossus" +- "github.com/prebid/prebid-server/adapters/connectad" +- "github.com/prebid/prebid-server/adapters/consumable" +- "github.com/prebid/prebid-server/adapters/conversant" +- "github.com/prebid/prebid-server/adapters/cpmstar" +- "github.com/prebid/prebid-server/adapters/datablocks" +- "github.com/prebid/prebid-server/adapters/decenterads" +- "github.com/prebid/prebid-server/adapters/deepintent" +- "github.com/prebid/prebid-server/adapters/dmx" +- "github.com/prebid/prebid-server/adapters/emx_digital" +- "github.com/prebid/prebid-server/adapters/engagebdr" +- "github.com/prebid/prebid-server/adapters/eplanning" +- "github.com/prebid/prebid-server/adapters/gamma" +- "github.com/prebid/prebid-server/adapters/gamoshi" +- "github.com/prebid/prebid-server/adapters/grid" +- "github.com/prebid/prebid-server/adapters/gumgum" +- "github.com/prebid/prebid-server/adapters/improvedigital" +- "github.com/prebid/prebid-server/adapters/inmobi" +- "github.com/prebid/prebid-server/adapters/invibes" +- "github.com/prebid/prebid-server/adapters/ix" +- "github.com/prebid/prebid-server/adapters/kidoz" +- "github.com/prebid/prebid-server/adapters/krushmedia" +- "github.com/prebid/prebid-server/adapters/kubient" +- "github.com/prebid/prebid-server/adapters/lockerdome" +- "github.com/prebid/prebid-server/adapters/logicad" +- "github.com/prebid/prebid-server/adapters/lunamedia" +- "github.com/prebid/prebid-server/adapters/marsmedia" +- "github.com/prebid/prebid-server/adapters/mgid" +- "github.com/prebid/prebid-server/adapters/mobfoxpb" +- "github.com/prebid/prebid-server/adapters/mobilefuse" +- "github.com/prebid/prebid-server/adapters/nanointeractive" +- "github.com/prebid/prebid-server/adapters/ninthdecimal" +- "github.com/prebid/prebid-server/adapters/nobid" +- "github.com/prebid/prebid-server/adapters/openx" +- "github.com/prebid/prebid-server/adapters/orbidder" +- "github.com/prebid/prebid-server/adapters/pubmatic" +- "github.com/prebid/prebid-server/adapters/pubnative" +- "github.com/prebid/prebid-server/adapters/pulsepoint" +- "github.com/prebid/prebid-server/adapters/revcontent" +- "github.com/prebid/prebid-server/adapters/rhythmone" +- "github.com/prebid/prebid-server/adapters/rtbhouse" +- "github.com/prebid/prebid-server/adapters/rubicon" +- "github.com/prebid/prebid-server/adapters/sharethrough" +- "github.com/prebid/prebid-server/adapters/silvermob" +- "github.com/prebid/prebid-server/adapters/smaato" +- "github.com/prebid/prebid-server/adapters/smartadserver" +- "github.com/prebid/prebid-server/adapters/smartrtb" +- "github.com/prebid/prebid-server/adapters/smartyads" +- "github.com/prebid/prebid-server/adapters/somoaudience" +- "github.com/prebid/prebid-server/adapters/sonobi" +- "github.com/prebid/prebid-server/adapters/sovrn" +- "github.com/prebid/prebid-server/adapters/synacormedia" +- "github.com/prebid/prebid-server/adapters/tappx" +- "github.com/prebid/prebid-server/adapters/telaria" +- "github.com/prebid/prebid-server/adapters/triplelift" +- "github.com/prebid/prebid-server/adapters/triplelift_native" +- "github.com/prebid/prebid-server/adapters/ucfunnel" +- "github.com/prebid/prebid-server/adapters/unruly" +- "github.com/prebid/prebid-server/adapters/valueimpression" +- "github.com/prebid/prebid-server/adapters/verizonmedia" +- "github.com/prebid/prebid-server/adapters/visx" +- "github.com/prebid/prebid-server/adapters/vrtcal" +- "github.com/prebid/prebid-server/adapters/yeahmobi" +- "github.com/prebid/prebid-server/adapters/yieldlab" +- "github.com/prebid/prebid-server/adapters/yieldmo" +- "github.com/prebid/prebid-server/adapters/yieldone" +- "github.com/prebid/prebid-server/adapters/zeroclickfraud" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ ttx "github.com/PubMatic-OpenWrap/prebid-server/adapters/33across" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/acuityads" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adgeneration" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adhese" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernel" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernelAdn" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adman" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/admixer" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adocean" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adoppler" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adot" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adpone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adprime" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtarget" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtelligent" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/advangelists" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/aja" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/amx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/applogy" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/avocet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/beachfront" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/beintoo" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/between" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/brightroll" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/colossus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/connectad" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/consumable" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/cpmstar" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/datablocks" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/decenterads" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/deepintent" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/dmx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/emx_digital" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/engagebdr" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/eplanning" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamma" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamoshi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/grid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gumgum" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/improvedigital" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/inmobi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/invibes" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/kidoz" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/krushmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/kubient" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lockerdome" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/logicad" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lunamedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/marsmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/mgid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/mobfoxpb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/mobilefuse" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/nanointeractive" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ninthdecimal" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/nobid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/openx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/orbidder" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubnative" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/revcontent" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rhythmone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rtbhouse" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sharethrough" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/silvermob" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smaato" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartadserver" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartyads" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/somoaudience" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sonobi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/synacormedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/tappx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/telaria" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift_native" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ucfunnel" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/unruly" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/valueimpression" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/verizonmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/visx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/vrtcal" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yeahmobi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldlab" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldmo" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/zeroclickfraud" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Adapter registration is kept in this separate file for ease of use and to aid +diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go +index 7e271376..4cd9c6dd 100644 +--- a/exchange/adapter_util.go ++++ b/exchange/adapter_util.go +@@ -4,12 +4,12 @@ import ( + "fmt" + "net/http" + +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/lifestreet" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func BuildAdapters(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) { +diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go +index 92eb0129..05cdd7e9 100644 +--- a/exchange/adapter_util_test.go ++++ b/exchange/adapter_util_test.go +@@ -6,15 +6,15 @@ import ( + "net/http" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/appnexus" +- "github.com/prebid/prebid-server/adapters/lifestreet" +- "github.com/prebid/prebid-server/adapters/rubicon" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" +- metrics "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ metrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/auction.go b/exchange/auction.go +index 97f82d3c..1cdb7b21 100644 +--- a/exchange/auction.go ++++ b/exchange/auction.go +@@ -11,10 +11,10 @@ import ( + "time" + + uuid "github.com/gofrs/uuid" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + ) + + type DebugLog struct { +diff --git a/exchange/auction_test.go b/exchange/auction_test.go +index 7e8a8a8c..ba9f1207 100644 +--- a/exchange/auction_test.go ++++ b/exchange/auction_test.go +@@ -11,11 +11,11 @@ import ( + "strconv" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/bidder.go b/exchange/bidder.go +index 5b5852bd..d69b7b85 100644 +--- a/exchange/bidder.go ++++ b/exchange/bidder.go +@@ -13,17 +13,17 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/config/util" +- "github.com/prebid/prebid-server/currency" +- +- "github.com/mxmCherry/openrtb" +- nativeRequests "github.com/mxmCherry/openrtb/native/request" +- nativeResponse "github.com/mxmCherry/openrtb/native/response" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config/util" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ ++ "github.com/PubMatic-OpenWrap/openrtb" ++ nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" ++ nativeResponse "github.com/PubMatic-OpenWrap/openrtb/native/response" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "golang.org/x/net/context/ctxhttp" + ) + +diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go +index b809c7e0..d4e8bfd2 100644 +--- a/exchange/bidder_test.go ++++ b/exchange/bidder_test.go +@@ -16,18 +16,18 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/metrics" +- metricsConfig "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + +- nativeRequests "github.com/mxmCherry/openrtb/native/request" +- nativeResponse "github.com/mxmCherry/openrtb/native/response" ++ nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" ++ nativeResponse "github.com/PubMatic-OpenWrap/openrtb/native/response" + ) + + // TestSingleBidder makes sure that the following things work if the Bidder needs only one request. +diff --git a/exchange/bidder_validate_bids.go b/exchange/bidder_validate_bids.go +index cf74dfb1..9ea35733 100644 +--- a/exchange/bidder_validate_bids.go ++++ b/exchange/bidder_validate_bids.go +@@ -6,10 +6,10 @@ import ( + "fmt" + "strings" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + goCurrency "golang.org/x/text/currency" + ) + +diff --git a/exchange/bidder_validate_bids_test.go b/exchange/bidder_validate_bids_test.go +index e7fc0b04..a2ec026f 100644 +--- a/exchange/bidder_validate_bids_test.go ++++ b/exchange/bidder_validate_bids_test.go +@@ -4,10 +4,10 @@ import ( + "context" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/events.go b/exchange/events.go +index 3a05e098..aa44d0b5 100644 +--- a/exchange/events.go ++++ b/exchange/events.go +@@ -5,12 +5,12 @@ import ( + "time" + + jsonpatch "github.com/evanphx/json-patch" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/endpoints/events" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // eventTracking has configuration fields needed for adding event tracking to an auction response +diff --git a/exchange/events_test.go b/exchange/events_test.go +index 1d72739d..3315dfaa 100644 +--- a/exchange/events_test.go ++++ b/exchange/events_test.go +@@ -3,8 +3,8 @@ package exchange + import ( + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/exchange.go b/exchange/exchange.go +index cfb9c6d3..5f3c0834 100644 +--- a/exchange/exchange.go ++++ b/exchange/exchange.go +@@ -14,18 +14,18 @@ import ( + "strings" + "time" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + ) + + type ContextKey string +diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go +index 8bdf6c45..9ebcda8f 100644 +--- a/exchange/exchange_test.go ++++ b/exchange/exchange_test.go +@@ -15,20 +15,20 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- metricsConf "github.com/prebid/prebid-server/metrics/config" +- metricsConfig "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- pbc "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/file_fetcher" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + "github.com/yudai/gojsondiff" + "github.com/yudai/gojsondiff/formatter" +@@ -74,15 +74,15 @@ func TestNewExchange(t *testing.T) { + // and check whether the returned request successfully prints any '&' characters as it should + // To do so, we: + // 1) Write the endpoint adapter URL with an '&' character into a new config,Configuration struct +-// as specified in https://github.com/prebid/prebid-server/issues/465 ++// as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 + // 2) Initialize a new exchange with said configuration + // 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs including the +-// sample request as specified in https://github.com/prebid/prebid-server/issues/465 ++// sample request as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 + // 4) Build a BidResponse struct using exchange.buildBidResponse(ctx.Background(), liveA... ) + // 5) Assert we have no '&' characters in the response that exchange.buildBidResponse returns + func TestCharacterEscape(t *testing.T) { + /* 1) Adapter with a '& char in its endpoint property */ +- /* https://github.com/prebid/prebid-server/issues/465 */ ++ /* https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 */ + cfg := &config.Configuration{ + Adapters: make(map[string]config.Adapter, 1), + } +@@ -114,7 +114,7 @@ func TestCharacterEscape(t *testing.T) { + adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid, 1) + adapterBids["appnexus"] = &pbsOrtbSeatBid{currency: "USD"} + +- //An openrtb.BidRequest struct as specified in https://github.com/prebid/prebid-server/issues/465 ++ //An openrtb.BidRequest struct as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{{ +diff --git a/exchange/gdpr.go b/exchange/gdpr.go +index e861f948..6b6a073b 100644 +--- a/exchange/gdpr.go ++++ b/exchange/gdpr.go +@@ -3,8 +3,8 @@ package exchange + import ( + "encoding/json" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + ) + + // ExtractGDPR will pull the gdpr flag from an openrtb request +diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go +index 35193909..e767c269 100644 +--- a/exchange/gdpr_test.go ++++ b/exchange/gdpr_test.go +@@ -4,8 +4,8 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/legacy.go b/exchange/legacy.go +index b4845b76..2a92dc55 100644 +--- a/exchange/legacy.go ++++ b/exchange/legacy.go +@@ -6,12 +6,12 @@ import ( + "errors" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // AdaptLegacyAdapter turns a bidder.Adapter into an adaptedBidder. +diff --git a/exchange/legacy_test.go b/exchange/legacy_test.go +index 2cef4fea..52bb1067 100644 +--- a/exchange/legacy_test.go ++++ b/exchange/legacy_test.go +@@ -11,12 +11,12 @@ import ( + + "github.com/buger/jsonparser" + jsonpatch "github.com/evanphx/json-patch" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + func TestSiteVideo(t *testing.T) { +diff --git a/exchange/price_granularity.go b/exchange/price_granularity.go +index 242d420f..ffcce061 100644 +--- a/exchange/price_granularity.go ++++ b/exchange/price_granularity.go +@@ -4,7 +4,7 @@ import ( + "math" + "strconv" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // GetPriceBucket is the externally facing function for computing CPM buckets +diff --git a/exchange/price_granularity_test.go b/exchange/price_granularity_test.go +index 13840838..6dccc677 100644 +--- a/exchange/price_granularity_test.go ++++ b/exchange/price_granularity_test.go +@@ -4,7 +4,7 @@ import ( + "math" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/targeting.go b/exchange/targeting.go +index 87bec6f3..6131f055 100644 +--- a/exchange/targeting.go ++++ b/exchange/targeting.go +@@ -3,8 +3,8 @@ package exchange + import ( + "strconv" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const MaxKeyLength = 20 +diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go +index 2a77c4f7..e6db9215 100644 +--- a/exchange/targeting_test.go ++++ b/exchange/targeting_test.go +@@ -8,17 +8,17 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" + +- "github.com/prebid/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + +- metricsConf "github.com/prebid/prebid-server/metrics/config" +- metricsConfig "github.com/prebid/prebid-server/metrics/config" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/exchange/utils.go b/exchange/utils.go +index 8d4c0fac..bc8a84ba 100644 +--- a/exchange/utils.go ++++ b/exchange/utils.go +@@ -9,14 +9,14 @@ import ( + "github.com/prebid/go-gdpr/vendorconsent" + + "github.com/buger/jsonparser" +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/privacy" +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/lmt" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/lmt" + ) + + var integrationTypeMap = map[metrics.RequestType]config.IntegrationType{ +diff --git a/exchange/utils_test.go b/exchange/utils_test.go +index 0407b3c5..e222103e 100644 +--- a/exchange/utils_test.go ++++ b/exchange/utils_test.go +@@ -7,12 +7,12 @@ import ( + "fmt" + "testing" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/gdpr" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go +index f24fd6c5..6d084543 100644 +--- a/gdpr/gdpr.go ++++ b/gdpr/gdpr.go +@@ -6,9 +6,9 @@ import ( + "strconv" + + "github.com/prebid/go-gdpr/vendorlist" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/errortypes" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + type Permissions interface { +diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go +index 5048cf11..81d3c615 100644 +--- a/gdpr/gdpr_test.go ++++ b/gdpr/gdpr_test.go +@@ -5,8 +5,8 @@ import ( + "net/http" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "github.com/stretchr/testify/assert" + ) +diff --git a/gdpr/impl.go b/gdpr/impl.go +index 06b625da..6561c939 100644 +--- a/gdpr/impl.go ++++ b/gdpr/impl.go +@@ -10,12 +10,12 @@ import ( + "github.com/prebid/go-gdpr/vendorconsent" + tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" + "github.com/prebid/go-gdpr/vendorlist" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // This file implements GDPR permissions for the app. +-// For more info, see https://github.com/prebid/prebid-server/issues/501 ++// For more info, see https://github.com/PubMatic-OpenWrap/prebid-server/issues/501 + // + // Nothing in this file is exported. Public APIs can be found in gdpr.go + +diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go +index 45d2ba43..1977c4cf 100644 +--- a/gdpr/impl_test.go ++++ b/gdpr/impl_test.go +@@ -6,8 +6,8 @@ import ( + "fmt" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "github.com/prebid/go-gdpr/vendorlist" + "github.com/prebid/go-gdpr/vendorlist2" +diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go +index 95a17109..62131ff4 100644 +--- a/gdpr/vendorlist-fetching.go ++++ b/gdpr/vendorlist-fetching.go +@@ -14,7 +14,7 @@ import ( + "github.com/prebid/go-gdpr/api" + "github.com/prebid/go-gdpr/vendorlist" + "github.com/prebid/go-gdpr/vendorlist2" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + "golang.org/x/net/context/ctxhttp" + ) + +@@ -22,7 +22,7 @@ type saveVendors func(uint16, api.VendorList) + + // This file provides the vendorlist-fetching function for Prebid Server. + // +-// For more info, see https://github.com/prebid/prebid-server/issues/504 ++// For more info, see https://github.com/PubMatic-OpenWrap/prebid-server/issues/504 + // + // Nothing in this file is exported. Public APIs can be found in gdpr.go + +diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go +index 77f3f294..01856c57 100644 +--- a/gdpr/vendorlist-fetching_test.go ++++ b/gdpr/vendorlist-fetching_test.go +@@ -11,7 +11,7 @@ import ( + "github.com/stretchr/testify/assert" + + "github.com/prebid/go-gdpr/consentconstants" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + func TestTCF1FetcherInitialLoad(t *testing.T) { +diff --git a/go.mod b/go.mod +index 48fc6b64..0e7d4398 100644 +--- a/go.mod ++++ b/go.mod +@@ -1,4 +1,4 @@ +-module github.com/prebid/prebid-server ++module github.com/PubMatic-OpenWrap/prebid-server + + go 1.14 + +@@ -30,7 +30,7 @@ require ( + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.0.0 // indirect + github.com/mssola/user_agent v0.4.1 // indirect +- github.com/mxmCherry/openrtb v11.0.0+incompatible ++ github.com/PubMatic-OpenWrap/openrtb v11.0.0+incompatible + github.com/onsi/ginkgo v1.10.1 // indirect + github.com/onsi/gomega v1.7.0 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect +diff --git a/go.sum b/go.sum +index 6da3f889..9318b9ff 100644 +--- a/go.sum ++++ b/go.sum +@@ -67,8 +67,8 @@ github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KH + github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= + github.com/mssola/user_agent v0.4.1 h1:iTUaMpVrb2qWyvUw8UvK3ygWMd2lB1NGuZ1xhpBf1eg= + github.com/mssola/user_agent v0.4.1/go.mod h1:UFiKPVaShrJGW93n4uo8dpPdg1BSVpw2P9bneo0Mtp8= +-github.com/mxmCherry/openrtb v11.0.0+incompatible h1:tNzh7vKwQ8lopBAadyN3QPryawXSaVXYWi1IVluXHiM= +-github.com/mxmCherry/openrtb v11.0.0+incompatible/go.mod h1:zpnz6Au3bzTGplpRU0kvFPNT6g4ROAKx/GkrslFDwZk= ++github.com/PubMatic-OpenWrap/openrtb v11.0.0+incompatible h1:tNzh7vKwQ8lopBAadyN3QPryawXSaVXYWi1IVluXHiM= ++github.com/PubMatic-OpenWrap/openrtb v11.0.0+incompatible/go.mod h1:zpnz6Au3bzTGplpRU0kvFPNT6g4ROAKx/GkrslFDwZk= + github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= + github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= + github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +diff --git a/main.go b/main.go +index 2ae75a2e..bca61563 100644 +--- a/main.go ++++ b/main.go +@@ -6,12 +6,12 @@ import ( + "net/http" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/currency" +- pbc "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/router" +- "github.com/prebid/prebid-server/server" +- "github.com/prebid/prebid-server/util/task" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/router" ++ "github.com/PubMatic-OpenWrap/prebid-server/server" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/task" + + "github.com/golang/glog" + "github.com/spf13/viper" +diff --git a/main_test.go b/main_test.go +index 70eea282..d4f96a83 100644 +--- a/main_test.go ++++ b/main_test.go +@@ -4,7 +4,7 @@ import ( + "os" + "testing" + +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/stretchr/testify/assert" + + "github.com/spf13/viper" +diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go +index c1f726e9..c828c28f 100644 +--- a/metrics/config/metrics.go ++++ b/metrics/config/metrics.go +@@ -3,10 +3,10 @@ package config + import ( + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- prometheusmetrics "github.com/prebid/prebid-server/metrics/prometheus" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ prometheusmetrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/prometheus" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + gometrics "github.com/rcrowley/go-metrics" + influxdb "github.com/vrischmann/go-metrics-influxdb" + ) +diff --git a/metrics/config/metrics_test.go b/metrics/config/metrics_test.go +index 5b70b53b..5465061d 100644 +--- a/metrics/config/metrics_test.go ++++ b/metrics/config/metrics_test.go +@@ -4,9 +4,9 @@ import ( + "testing" + "time" + +- mainConfig "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ mainConfig "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + gometrics "github.com/rcrowley/go-metrics" + ) + +diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go +index ac7ed069..f9e19365 100644 +--- a/metrics/go_metrics.go ++++ b/metrics/go_metrics.go +@@ -6,8 +6,8 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + metrics "github.com/rcrowley/go-metrics" + ) + +diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go +index 2d0b9097..712e8d52 100644 +--- a/metrics/go_metrics_test.go ++++ b/metrics/go_metrics_test.go +@@ -4,8 +4,8 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + metrics "github.com/rcrowley/go-metrics" + "github.com/stretchr/testify/assert" + ) +diff --git a/metrics/metrics.go b/metrics/metrics.go +index 62de3afa..4840e67d 100644 +--- a/metrics/metrics.go ++++ b/metrics/metrics.go +@@ -3,7 +3,7 @@ package metrics + import ( + "time" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Labels defines the labels that can be attached to the metrics. +diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go +index c852eb47..62979d6d 100644 +--- a/metrics/metrics_mock.go ++++ b/metrics/metrics_mock.go +@@ -3,7 +3,7 @@ package metrics + import ( + "time" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/mock" + ) + +diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go +index f4dfe434..0f2ada29 100644 +--- a/metrics/prometheus/preload.go ++++ b/metrics/prometheus/preload.go +@@ -1,7 +1,7 @@ + package prometheusmetrics + + import ( +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/prometheus/client_golang/prometheus" + ) + +diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go +index 1e384c0e..a43408ce 100644 +--- a/metrics/prometheus/prometheus.go ++++ b/metrics/prometheus/prometheus.go +@@ -4,9 +4,9 @@ import ( + "strconv" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/prometheus/client_golang/prometheus" + ) + +diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go +index b2ffff34..5f76a182 100644 +--- a/metrics/prometheus/prometheus_test.go ++++ b/metrics/prometheus/prometheus_test.go +@@ -5,9 +5,9 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" +diff --git a/metrics/prometheus/type_conversion.go b/metrics/prometheus/type_conversion.go +index 0e5c8063..eff379e8 100644 +--- a/metrics/prometheus/type_conversion.go ++++ b/metrics/prometheus/type_conversion.go +@@ -3,8 +3,8 @@ package prometheusmetrics + import ( + "strconv" + +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func actionsAsString() []string { +diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go +index 09edf51d..13ec8eb4 100644 +--- a/openrtb_ext/bid_request_video.go ++++ b/openrtb_ext/bid_request_video.go +@@ -1,7 +1,7 @@ + package openrtb_ext + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + type BidRequestVideo struct { +diff --git a/openrtb_ext/deal_tier.go b/openrtb_ext/deal_tier.go +index 8583c053..e882235d 100644 +--- a/openrtb_ext/deal_tier.go ++++ b/openrtb_ext/deal_tier.go +@@ -3,7 +3,7 @@ package openrtb_ext + import ( + "encoding/json" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // DealTier defines the configuration of a deal tier. +diff --git a/openrtb_ext/deal_tier_test.go b/openrtb_ext/deal_tier_test.go +index 1bb4d564..717e0703 100644 +--- a/openrtb_ext/deal_tier_test.go ++++ b/openrtb_ext/deal_tier_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/openrtb_ext/device.go b/openrtb_ext/device.go +index b3fd6cbb..9179c9c9 100644 +--- a/openrtb_ext/device.go ++++ b/openrtb_ext/device.go +@@ -4,7 +4,7 @@ import ( + "strconv" + + "github.com/buger/jsonparser" +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + ) + + // PrebidExtKey represents the prebid extension key used in requests +diff --git a/openrtb_ext/device_test.go b/openrtb_ext/device_test.go +index e307374e..b4c85bcc 100644 +--- a/openrtb_ext/device_test.go ++++ b/openrtb_ext/device_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go +index 02370d19..f2119f2c 100644 +--- a/openrtb_ext/response.go ++++ b/openrtb_ext/response.go +@@ -1,7 +1,7 @@ + package openrtb_ext + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // ExtBidResponse defines the contract for bidresponse.ext +diff --git a/openrtb_ext/site_test.go b/openrtb_ext/site_test.go +index 67ec6cc4..0d41e0c0 100644 +--- a/openrtb_ext/site_test.go ++++ b/openrtb_ext/site_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/pbs/pbsrequest.go b/pbs/pbsrequest.go +index 30f8bd25..b51b25ef 100644 +--- a/pbs/pbsrequest.go ++++ b/pbs/pbsrequest.go +@@ -10,17 +10,17 @@ import ( + "strings" + "time" + +- "github.com/prebid/prebid-server/cache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/usersync" +- "github.com/prebid/prebid-server/util/httputil" +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/httputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + + "github.com/blang/semver" + "github.com/buger/jsonparser" + "github.com/golang/glog" +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "golang.org/x/net/publicsuffix" + ) + +diff --git a/pbs/pbsrequest_test.go b/pbs/pbsrequest_test.go +index 52cd6153..29c40cec 100644 +--- a/pbs/pbsrequest_test.go ++++ b/pbs/pbsrequest_test.go +@@ -9,8 +9,8 @@ import ( + "testing" + + "github.com/magiconair/properties/assert" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + const mimeVideoMp4 = "video/mp4" +diff --git a/pbs/usersync.go b/pbs/usersync.go +index 4cac3544..52316f3e 100644 +--- a/pbs/usersync.go ++++ b/pbs/usersync.go +@@ -11,11 +11,11 @@ import ( + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/analytics" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/server/ssl" +- "github.com/prebid/prebid-server/usersync" ++ "github.com/PubMatic-OpenWrap/prebid-server/analytics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // Recaptcha code from https://github.com/haisum/recaptcha/blob/master/recaptcha.go +diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go +index 88c197cd..8d363e14 100644 +--- a/prebid_cache_client/client.go ++++ b/prebid_cache_client/client.go +@@ -12,8 +12,8 @@ import ( + "strings" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + + "github.com/buger/jsonparser" + "github.com/golang/glog" +diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go +index 1ba30a6f..c4eeca82 100644 +--- a/prebid_cache_client/client_test.go ++++ b/prebid_cache_client/client_test.go +@@ -10,9 +10,9 @@ import ( + "strconv" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- metricsConf "github.com/prebid/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +diff --git a/privacy/ccpa/consentwriter.go b/privacy/ccpa/consentwriter.go +index 48566554..4ef412fd 100644 +--- a/privacy/ccpa/consentwriter.go ++++ b/privacy/ccpa/consentwriter.go +@@ -1,7 +1,7 @@ + package ccpa + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // ConsentWriter implements the PolicyWriter interface for CCPA. +diff --git a/privacy/ccpa/consentwriter_test.go b/privacy/ccpa/consentwriter_test.go +index 57a7f8f4..1e491d9d 100644 +--- a/privacy/ccpa/consentwriter_test.go ++++ b/privacy/ccpa/consentwriter_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/privacy/ccpa/parsedpolicy.go b/privacy/ccpa/parsedpolicy.go +index 3c934e67..52977104 100644 +--- a/privacy/ccpa/parsedpolicy.go ++++ b/privacy/ccpa/parsedpolicy.go +@@ -4,7 +4,7 @@ import ( + "errors" + "fmt" + +- "github.com/prebid/prebid-server/errortypes" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + ) + + const ( +diff --git a/privacy/ccpa/parsedpolicy_test.go b/privacy/ccpa/parsedpolicy_test.go +index 2f7e8bfd..4fa9f926 100644 +--- a/privacy/ccpa/parsedpolicy_test.go ++++ b/privacy/ccpa/parsedpolicy_test.go +@@ -3,7 +3,7 @@ package ccpa + import ( + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + ) +diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go +index a9f1c49e..3f5dd25c 100644 +--- a/privacy/ccpa/policy.go ++++ b/privacy/ccpa/policy.go +@@ -5,8 +5,8 @@ import ( + "errors" + "fmt" + +- "github.com/mxmCherry/openrtb" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/openrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + // Policy represents the CCPA regulatory information from an OpenRTB bid request. +diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go +index 7ff896e9..c1fdd9cd 100644 +--- a/privacy/ccpa/policy_test.go ++++ b/privacy/ccpa/policy_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/privacy/enforcement.go b/privacy/enforcement.go +index 64070ae3..47c1f0d0 100644 +--- a/privacy/enforcement.go ++++ b/privacy/enforcement.go +@@ -1,7 +1,7 @@ + package privacy + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // Enforcement represents the privacy policies to enforce for an OpenRTB bid request. +diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go +index 9240aafc..a5e41c83 100644 +--- a/privacy/enforcement_test.go ++++ b/privacy/enforcement_test.go +@@ -3,7 +3,7 @@ package privacy + import ( + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + ) +diff --git a/privacy/gdpr/consentwriter.go b/privacy/gdpr/consentwriter.go +index 040bbd6c..f1cc2ce1 100644 +--- a/privacy/gdpr/consentwriter.go ++++ b/privacy/gdpr/consentwriter.go +@@ -3,9 +3,9 @@ package gdpr + import ( + "encoding/json" + +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // ConsentWriter implements the PolicyWriter interface for GDPR TCF. +diff --git a/privacy/gdpr/consentwriter_test.go b/privacy/gdpr/consentwriter_test.go +index 77fbdf88..65df8051 100644 +--- a/privacy/gdpr/consentwriter_test.go ++++ b/privacy/gdpr/consentwriter_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/privacy/lmt/policy.go b/privacy/lmt/policy.go +index 295dcc46..5f23b9a3 100644 +--- a/privacy/lmt/policy.go ++++ b/privacy/lmt/policy.go +@@ -1,7 +1,7 @@ + package lmt + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + const ( +diff --git a/privacy/lmt/policy_test.go b/privacy/lmt/policy_test.go +index 3027414f..9d0e3b6a 100644 +--- a/privacy/lmt/policy_test.go ++++ b/privacy/lmt/policy_test.go +@@ -3,7 +3,7 @@ package lmt + import ( + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/privacy/policies.go b/privacy/policies.go +index bc844a4e..a1c3fca4 100644 +--- a/privacy/policies.go ++++ b/privacy/policies.go +@@ -1,9 +1,9 @@ + package privacy + + import ( +- "github.com/prebid/prebid-server/privacy/ccpa" +- "github.com/prebid/prebid-server/privacy/gdpr" +- "github.com/prebid/prebid-server/privacy/lmt" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" ++ "github.com/PubMatic-OpenWrap/prebid-server/privacy/lmt" + ) + + // Policies represents the privacy regulations for an OpenRTB bid request. +diff --git a/privacy/scrubber.go b/privacy/scrubber.go +index 8771c8b3..aea5c900 100644 +--- a/privacy/scrubber.go ++++ b/privacy/scrubber.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "strings" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // ScrubStrategyIPV4 defines the approach to scrub PII from an IPV4 address. +diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go +index e0a2cb86..4d989e1c 100644 +--- a/privacy/scrubber_test.go ++++ b/privacy/scrubber_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/privacy/writer.go b/privacy/writer.go +index c61767f1..a68a158c 100644 +--- a/privacy/writer.go ++++ b/privacy/writer.go +@@ -1,7 +1,7 @@ + package privacy + + import ( +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + ) + + // PolicyWriter mutates an OpenRTB bid request with a policy's regulatory information. +diff --git a/privacy/writer_test.go b/privacy/writer_test.go +index 79170cfc..754e6ffe 100644 +--- a/privacy/writer_test.go ++++ b/privacy/writer_test.go +@@ -4,7 +4,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/mxmCherry/openrtb" ++ "github.com/PubMatic-OpenWrap/openrtb" + "github.com/stretchr/testify/assert" + ) + +diff --git a/router/admin.go b/router/admin.go +index b66bf55a..ba4f25f5 100644 +--- a/router/admin.go ++++ b/router/admin.go +@@ -5,8 +5,8 @@ import ( + "net/http/pprof" + "time" + +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/endpoints" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints" + ) + + func Admin(revision string, rateConverter *currency.RateConverter, rateConverterFetchingInterval time.Duration) *http.ServeMux { +diff --git a/router/aspects/request_timeout_handler.go b/router/aspects/request_timeout_handler.go +index 39a4341f..19839f01 100644 +--- a/router/aspects/request_timeout_handler.go ++++ b/router/aspects/request_timeout_handler.go +@@ -6,8 +6,8 @@ import ( + "time" + + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + ) + + func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders, metricsEngine metrics.MetricsEngine, requestType metrics.RequestType) httprouter.Handle { +diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go +index 26e546dc..b9a56e57 100644 +--- a/router/aspects/request_timeout_handler_test.go ++++ b/router/aspects/request_timeout_handler_test.go +@@ -8,8 +8,8 @@ import ( + "time" + + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + + "github.com/stretchr/testify/assert" + ) +diff --git a/router/router.go b/router/router.go +index 48580f85..ab76c02e 100644 +--- a/router/router.go ++++ b/router/router.go +@@ -12,41 +12,41 @@ import ( + "strings" + "time" + +- "github.com/prebid/prebid-server/currency" +- "github.com/prebid/prebid-server/endpoints/events" +- "github.com/prebid/prebid-server/errortypes" +- +- "github.com/prebid/prebid-server/metrics" +- +- "github.com/prebid/prebid-server/adapters" +- "github.com/prebid/prebid-server/adapters/adform" +- "github.com/prebid/prebid-server/adapters/appnexus" +- "github.com/prebid/prebid-server/adapters/conversant" +- "github.com/prebid/prebid-server/adapters/ix" +- "github.com/prebid/prebid-server/adapters/lifestreet" +- "github.com/prebid/prebid-server/adapters/pubmatic" +- "github.com/prebid/prebid-server/adapters/pulsepoint" +- "github.com/prebid/prebid-server/adapters/rubicon" +- "github.com/prebid/prebid-server/adapters/sovrn" +- analyticsConf "github.com/prebid/prebid-server/analytics/config" +- "github.com/prebid/prebid-server/cache" +- "github.com/prebid/prebid-server/cache/dummycache" +- "github.com/prebid/prebid-server/cache/filecache" +- "github.com/prebid/prebid-server/cache/postgrescache" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/endpoints" +- infoEndpoints "github.com/prebid/prebid-server/endpoints/info" +- "github.com/prebid/prebid-server/endpoints/openrtb2" +- "github.com/prebid/prebid-server/exchange" +- "github.com/prebid/prebid-server/gdpr" +- metricsConf "github.com/prebid/prebid-server/metrics/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/pbs" +- pbc "github.com/prebid/prebid-server/prebid_cache_client" +- "github.com/prebid/prebid-server/router/aspects" +- "github.com/prebid/prebid-server/server/ssl" +- storedRequestsConf "github.com/prebid/prebid-server/stored_requests/config" +- "github.com/prebid/prebid-server/usersync/usersyncers" ++ "github.com/PubMatic-OpenWrap/prebid-server/currency" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ++ ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" ++ analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/filecache" ++ "github.com/PubMatic-OpenWrap/prebid-server/cache/postgrescache" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints" ++ infoEndpoints "github.com/PubMatic-OpenWrap/prebid-server/endpoints/info" ++ "github.com/PubMatic-OpenWrap/prebid-server/endpoints/openrtb2" ++ "github.com/PubMatic-OpenWrap/prebid-server/exchange" ++ "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ++ metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/pbs" ++ pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" ++ "github.com/PubMatic-OpenWrap/prebid-server/router/aspects" ++ "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" ++ storedRequestsConf "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync/usersyncers" + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +diff --git a/router/router_test.go b/router/router_test.go +index a86d0f72..09b07a0a 100644 +--- a/router/router_test.go ++++ b/router/router_test.go +@@ -8,8 +8,8 @@ import ( + "os" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + + "github.com/stretchr/testify/assert" + ) +diff --git a/server/listener.go b/server/listener.go +index c1f57723..97e2e02e 100644 +--- a/server/listener.go ++++ b/server/listener.go +@@ -6,7 +6,7 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + ) + + // monitorableListener tracks any opened connections in the metrics. +diff --git a/server/listener_test.go b/server/listener_test.go +index f91dbddb..58f3d42d 100644 +--- a/server/listener_test.go ++++ b/server/listener_test.go +@@ -6,8 +6,8 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + gometrics "github.com/rcrowley/go-metrics" + ) + +diff --git a/server/prometheus.go b/server/prometheus.go +index 4b9f7037..8001234f 100644 +--- a/server/prometheus.go ++++ b/server/prometheus.go +@@ -7,9 +7,9 @@ import ( + "github.com/golang/glog" + "github.com/prometheus/client_golang/prometheus/promhttp" + +- "github.com/prebid/prebid-server/config" +- metricsconfig "github.com/prebid/prebid-server/metrics/config" +- prometheusMetrics "github.com/prebid/prebid-server/metrics/prometheus" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ metricsconfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ++ prometheusMetrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/prometheus" + ) + + func newPrometheusServer(cfg *config.Configuration, metrics *metricsconfig.DetailedMetricsEngine) *http.Server { +diff --git a/server/server.go b/server/server.go +index 46b7e5ae..5792a1b5 100644 +--- a/server/server.go ++++ b/server/server.go +@@ -13,9 +13,9 @@ import ( + + "github.com/NYTimes/gziphandler" + "github.com/golang/glog" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- metricsconfig "github.com/prebid/prebid-server/metrics/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ metricsconfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + ) + + // Listen blocks forever, serving PBS requests on the given port. This will block forever, until the process is shut down. +diff --git a/server/server_test.go b/server/server_test.go +index e7ef593a..3d6d5684 100644 +--- a/server/server_test.go ++++ b/server/server_test.go +@@ -5,7 +5,7 @@ import ( + "os" + "testing" + +- "github.com/prebid/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" + ) + + func TestNewAdminServer(t *testing.T) { +diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go +index d8cf132d..85a7e645 100644 +--- a/stored_requests/backends/db_fetcher/fetcher.go ++++ b/stored_requests/backends/db_fetcher/fetcher.go +@@ -8,7 +8,7 @@ import ( + "github.com/lib/pq" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + func NewFetcher(db *sql.DB, queryMaker func(int, int) string) stored_requests.AllFetcher { +diff --git a/stored_requests/backends/empty_fetcher/fetcher.go b/stored_requests/backends/empty_fetcher/fetcher.go +index ee6b98b3..6edf3cc4 100644 +--- a/stored_requests/backends/empty_fetcher/fetcher.go ++++ b/stored_requests/backends/empty_fetcher/fetcher.go +@@ -4,7 +4,7 @@ import ( + "context" + "encoding/json" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + // EmptyFetcher is a nil-object which has no Stored Requests. +diff --git a/stored_requests/backends/file_fetcher/fetcher.go b/stored_requests/backends/file_fetcher/fetcher.go +index 2d3b0065..bff94b21 100644 +--- a/stored_requests/backends/file_fetcher/fetcher.go ++++ b/stored_requests/backends/file_fetcher/fetcher.go +@@ -7,7 +7,7 @@ import ( + "io/ioutil" + "strings" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + // NewFileFetcher _immediately_ loads stored request data from local files. +diff --git a/stored_requests/backends/file_fetcher/fetcher_test.go b/stored_requests/backends/file_fetcher/fetcher_test.go +index a145a3b4..f0900002 100644 +--- a/stored_requests/backends/file_fetcher/fetcher_test.go ++++ b/stored_requests/backends/file_fetcher/fetcher_test.go +@@ -6,7 +6,7 @@ import ( + "fmt" + "testing" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/stretchr/testify/assert" + ) + +diff --git a/stored_requests/backends/http_fetcher/fetcher.go b/stored_requests/backends/http_fetcher/fetcher.go +index bc12caec..5a7d8fa2 100644 +--- a/stored_requests/backends/http_fetcher/fetcher.go ++++ b/stored_requests/backends/http_fetcher/fetcher.go +@@ -10,7 +10,7 @@ import ( + "net/url" + "strings" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/golang/glog" + "golang.org/x/net/context/ctxhttp" +diff --git a/stored_requests/caches/cachestest/reliable.go b/stored_requests/caches/cachestest/reliable.go +index 7fbaf723..a0ab07df 100644 +--- a/stored_requests/caches/cachestest/reliable.go ++++ b/stored_requests/caches/cachestest/reliable.go +@@ -5,7 +5,7 @@ import ( + "encoding/json" + "testing" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + const ( +diff --git a/stored_requests/caches/memory/cache.go b/stored_requests/caches/memory/cache.go +index 5939c26d..565e9ba4 100644 +--- a/stored_requests/caches/memory/cache.go ++++ b/stored_requests/caches/memory/cache.go +@@ -7,7 +7,7 @@ import ( + + "github.com/coocood/freecache" + "github.com/golang/glog" +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + // NewCache returns an in-memory Cache which evicts items if: +diff --git a/stored_requests/caches/memory/cache_test.go b/stored_requests/caches/memory/cache_test.go +index b89bd5af..20ec1239 100644 +--- a/stored_requests/caches/memory/cache_test.go ++++ b/stored_requests/caches/memory/cache_test.go +@@ -7,8 +7,8 @@ import ( + "strconv" + "testing" + +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/caches/cachestest" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/cachestest" + ) + + func TestLRURobustness(t *testing.T) { +diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go +index 7f92f252..74f58ae3 100644 +--- a/stored_requests/config/config.go ++++ b/stored_requests/config/config.go +@@ -6,23 +6,23 @@ import ( + "net/http" + "time" + +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + + "github.com/golang/glog" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/backends/db_fetcher" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" +- "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" +- "github.com/prebid/prebid-server/stored_requests/caches/memory" +- "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" +- "github.com/prebid/prebid-server/stored_requests/events" +- apiEvents "github.com/prebid/prebid-server/stored_requests/events/api" +- httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" +- postgresEvents "github.com/prebid/prebid-server/stored_requests/events/postgres" +- "github.com/prebid/prebid-server/util/task" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/db_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/file_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/http_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/nil_cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" ++ apiEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/api" ++ httpEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/http" ++ postgresEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/postgres" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/task" + ) + + // This gets set to the connection string used when a database connection is made. We only support a single +diff --git a/stored_requests/config/config_test.go b/stored_requests/config/config_test.go +index 6c8cd612..8a1d64c0 100644 +--- a/stored_requests/config/config_test.go ++++ b/stored_requests/config/config_test.go +@@ -13,13 +13,13 @@ import ( + + sqlmock "github.com/DATA-DOG/go-sqlmock" + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" +- "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" +- "github.com/prebid/prebid-server/stored_requests/events" +- httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/http_fetcher" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" ++ httpEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/http" + "github.com/stretchr/testify/mock" + ) + +diff --git a/stored_requests/events/api/api.go b/stored_requests/events/api/api.go +index 6dce4eba..a37fadd3 100644 +--- a/stored_requests/events/api/api.go ++++ b/stored_requests/events/api/api.go +@@ -6,7 +6,7 @@ import ( + "net/http" + + "github.com/julienschmidt/httprouter" +- "github.com/prebid/prebid-server/stored_requests/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + ) + + type eventsAPI struct { +diff --git a/stored_requests/events/api/api_test.go b/stored_requests/events/api/api_test.go +index cd3af77b..74e02e69 100644 +--- a/stored_requests/events/api/api_test.go ++++ b/stored_requests/events/api/api_test.go +@@ -9,9 +9,9 @@ import ( + "strings" + "testing" + +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/caches/memory" +- "github.com/prebid/prebid-server/stored_requests/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + ) + + func TestGoodRequests(t *testing.T) { +diff --git a/stored_requests/events/events.go b/stored_requests/events/events.go +index 5b899435..60909a0d 100644 +--- a/stored_requests/events/events.go ++++ b/stored_requests/events/events.go +@@ -4,7 +4,7 @@ import ( + "context" + "encoding/json" + +- "github.com/prebid/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + ) + + // Save represents a bulk save +diff --git a/stored_requests/events/events_test.go b/stored_requests/events/events_test.go +index f3483705..0a48b4cc 100644 +--- a/stored_requests/events/events_test.go ++++ b/stored_requests/events/events_test.go +@@ -7,8 +7,8 @@ import ( + "reflect" + "testing" + +- "github.com/prebid/prebid-server/stored_requests" +- "github.com/prebid/prebid-server/stored_requests/caches/memory" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" + ) + + func TestListen(t *testing.T) { +diff --git a/stored_requests/events/http/http.go b/stored_requests/events/http/http.go +index 4615183f..5a0115f7 100644 +--- a/stored_requests/events/http/http.go ++++ b/stored_requests/events/http/http.go +@@ -12,7 +12,7 @@ import ( + "golang.org/x/net/context/ctxhttp" + + "github.com/buger/jsonparser" +- "github.com/prebid/prebid-server/stored_requests/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + + "github.com/golang/glog" + ) +diff --git a/stored_requests/events/postgres/database.go b/stored_requests/events/postgres/database.go +index e769a555..311e39ce 100644 +--- a/stored_requests/events/postgres/database.go ++++ b/stored_requests/events/postgres/database.go +@@ -9,10 +9,10 @@ import ( + "time" + + "github.com/golang/glog" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests/events" +- "github.com/prebid/prebid-server/util/timeutil" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/timeutil" + ) + + func bytesNull() []byte { +diff --git a/stored_requests/events/postgres/database_test.go b/stored_requests/events/postgres/database_test.go +index 15d0fbff..63625061 100644 +--- a/stored_requests/events/postgres/database_test.go ++++ b/stored_requests/events/postgres/database_test.go +@@ -7,9 +7,9 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests/events" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + +diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go +index 865231ee..597b660c 100644 +--- a/stored_requests/fetcher.go ++++ b/stored_requests/fetcher.go +@@ -5,7 +5,7 @@ import ( + "encoding/json" + "fmt" + +- "github.com/prebid/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" + ) + + // Fetcher knows how to fetch Stored Request data by id. +diff --git a/stored_requests/fetcher_test.go b/stored_requests/fetcher_test.go +index e77bc75c..07631e79 100644 +--- a/stored_requests/fetcher_test.go ++++ b/stored_requests/fetcher_test.go +@@ -6,8 +6,8 @@ import ( + "errors" + "testing" + +- "github.com/prebid/prebid-server/metrics" +- "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" ++ "github.com/PubMatic-OpenWrap/prebid-server/metrics" ++ "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/nil_cache" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +diff --git a/usersync/cookie.go b/usersync/cookie.go +index c4e329b6..ebe15d1c 100644 +--- a/usersync/cookie.go ++++ b/usersync/cookie.go +@@ -8,8 +8,8 @@ import ( + "net/http" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + const ( +diff --git a/usersync/cookie_test.go b/usersync/cookie_test.go +index ef2e9911..c05eadd4 100644 +--- a/usersync/cookie_test.go ++++ b/usersync/cookie_test.go +@@ -9,8 +9,8 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/stretchr/testify/assert" + ) + +diff --git a/usersync/usersync.go b/usersync/usersync.go +index 236acbe7..7730febc 100644 +--- a/usersync/usersync.go ++++ b/usersync/usersync.go +@@ -1,6 +1,6 @@ + package usersync + +-import "github.com/prebid/prebid-server/privacy" ++import "github.com/PubMatic-OpenWrap/prebid-server/privacy" + + type Usersyncer interface { + // GetUsersyncInfo returns basic info the browser needs in order to run a user sync. +diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go +index c41f7c6c..ec20ffbe 100755 +--- a/usersync/usersyncers/syncer.go ++++ b/usersync/usersyncers/syncer.go +@@ -5,86 +5,86 @@ import ( + "text/template" + + "github.com/golang/glog" +- ttx "github.com/prebid/prebid-server/adapters/33across" +- "github.com/prebid/prebid-server/adapters/acuityads" +- "github.com/prebid/prebid-server/adapters/adform" +- "github.com/prebid/prebid-server/adapters/adkernel" +- "github.com/prebid/prebid-server/adapters/adkernelAdn" +- "github.com/prebid/prebid-server/adapters/adman" +- "github.com/prebid/prebid-server/adapters/admixer" +- "github.com/prebid/prebid-server/adapters/adocean" +- "github.com/prebid/prebid-server/adapters/adpone" +- "github.com/prebid/prebid-server/adapters/adtarget" +- "github.com/prebid/prebid-server/adapters/adtelligent" +- "github.com/prebid/prebid-server/adapters/advangelists" +- "github.com/prebid/prebid-server/adapters/aja" +- "github.com/prebid/prebid-server/adapters/amx" +- "github.com/prebid/prebid-server/adapters/appnexus" +- "github.com/prebid/prebid-server/adapters/audienceNetwork" +- "github.com/prebid/prebid-server/adapters/avocet" +- "github.com/prebid/prebid-server/adapters/beachfront" +- "github.com/prebid/prebid-server/adapters/beintoo" +- "github.com/prebid/prebid-server/adapters/between" +- "github.com/prebid/prebid-server/adapters/brightroll" +- "github.com/prebid/prebid-server/adapters/colossus" +- "github.com/prebid/prebid-server/adapters/connectad" +- "github.com/prebid/prebid-server/adapters/consumable" +- "github.com/prebid/prebid-server/adapters/conversant" +- "github.com/prebid/prebid-server/adapters/cpmstar" +- "github.com/prebid/prebid-server/adapters/datablocks" +- "github.com/prebid/prebid-server/adapters/deepintent" +- "github.com/prebid/prebid-server/adapters/dmx" +- "github.com/prebid/prebid-server/adapters/emx_digital" +- "github.com/prebid/prebid-server/adapters/engagebdr" +- "github.com/prebid/prebid-server/adapters/eplanning" +- "github.com/prebid/prebid-server/adapters/gamma" +- "github.com/prebid/prebid-server/adapters/gamoshi" +- "github.com/prebid/prebid-server/adapters/grid" +- "github.com/prebid/prebid-server/adapters/gumgum" +- "github.com/prebid/prebid-server/adapters/improvedigital" +- "github.com/prebid/prebid-server/adapters/invibes" +- "github.com/prebid/prebid-server/adapters/ix" +- "github.com/prebid/prebid-server/adapters/krushmedia" +- "github.com/prebid/prebid-server/adapters/lifestreet" +- "github.com/prebid/prebid-server/adapters/lockerdome" +- "github.com/prebid/prebid-server/adapters/logicad" +- "github.com/prebid/prebid-server/adapters/lunamedia" +- "github.com/prebid/prebid-server/adapters/marsmedia" +- "github.com/prebid/prebid-server/adapters/mediafuse" +- "github.com/prebid/prebid-server/adapters/mgid" +- "github.com/prebid/prebid-server/adapters/nanointeractive" +- "github.com/prebid/prebid-server/adapters/ninthdecimal" +- "github.com/prebid/prebid-server/adapters/nobid" +- "github.com/prebid/prebid-server/adapters/openx" +- "github.com/prebid/prebid-server/adapters/pubmatic" +- "github.com/prebid/prebid-server/adapters/pulsepoint" +- "github.com/prebid/prebid-server/adapters/rhythmone" +- "github.com/prebid/prebid-server/adapters/rtbhouse" +- "github.com/prebid/prebid-server/adapters/rubicon" +- "github.com/prebid/prebid-server/adapters/sharethrough" +- "github.com/prebid/prebid-server/adapters/smartadserver" +- "github.com/prebid/prebid-server/adapters/smartrtb" +- "github.com/prebid/prebid-server/adapters/smartyads" +- "github.com/prebid/prebid-server/adapters/somoaudience" +- "github.com/prebid/prebid-server/adapters/sonobi" +- "github.com/prebid/prebid-server/adapters/sovrn" +- "github.com/prebid/prebid-server/adapters/synacormedia" +- "github.com/prebid/prebid-server/adapters/telaria" +- "github.com/prebid/prebid-server/adapters/triplelift" +- "github.com/prebid/prebid-server/adapters/triplelift_native" +- "github.com/prebid/prebid-server/adapters/ucfunnel" +- "github.com/prebid/prebid-server/adapters/unruly" +- "github.com/prebid/prebid-server/adapters/valueimpression" +- "github.com/prebid/prebid-server/adapters/verizonmedia" +- "github.com/prebid/prebid-server/adapters/visx" +- "github.com/prebid/prebid-server/adapters/vrtcal" +- "github.com/prebid/prebid-server/adapters/yieldlab" +- "github.com/prebid/prebid-server/adapters/yieldmo" +- "github.com/prebid/prebid-server/adapters/yieldone" +- "github.com/prebid/prebid-server/adapters/zeroclickfraud" +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" +- "github.com/prebid/prebid-server/usersync" ++ ttx "github.com/PubMatic-OpenWrap/prebid-server/adapters/33across" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/acuityads" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernel" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernelAdn" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adman" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/admixer" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adocean" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adpone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtarget" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtelligent" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/advangelists" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/aja" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/amx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/avocet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/beachfront" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/beintoo" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/between" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/brightroll" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/colossus" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/connectad" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/consumable" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/cpmstar" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/datablocks" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/deepintent" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/dmx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/emx_digital" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/engagebdr" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/eplanning" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamma" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamoshi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/grid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/gumgum" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/improvedigital" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/invibes" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/krushmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lockerdome" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/logicad" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/lunamedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/marsmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/mediafuse" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/mgid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/nanointeractive" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ninthdecimal" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/nobid" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/openx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rhythmone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rtbhouse" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sharethrough" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartadserver" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartrtb" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartyads" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/somoaudience" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sonobi" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/synacormedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/telaria" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift_native" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/ucfunnel" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/unruly" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/valueimpression" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/verizonmedia" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/visx" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/vrtcal" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldlab" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldmo" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldone" ++ "github.com/PubMatic-OpenWrap/prebid-server/adapters/zeroclickfraud" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/usersync" + ) + + // NewSyncerMap returns a map of all the usersyncer objects. +diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go +index 73f0e886..b15301fa 100755 +--- a/usersync/usersyncers/syncer_test.go ++++ b/usersync/usersyncers/syncer_test.go +@@ -4,8 +4,8 @@ import ( + "strings" + "testing" + +- "github.com/prebid/prebid-server/config" +- "github.com/prebid/prebid-server/openrtb_ext" ++ "github.com/PubMatic-OpenWrap/prebid-server/config" ++ "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + ) + + func TestNewSyncerMap(t *testing.T) { +diff --git a/util/httputil/httputil.go b/util/httputil/httputil.go +index 46151277..93bcca2a 100644 +--- a/util/httputil/httputil.go ++++ b/util/httputil/httputil.go +@@ -5,7 +5,7 @@ import ( + "net/http" + "strings" + +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + ) + + var ( +diff --git a/util/httputil/httputil_test.go b/util/httputil/httputil_test.go +index f7166740..7b6a9a50 100644 +--- a/util/httputil/httputil_test.go ++++ b/util/httputil/httputil_test.go +@@ -6,7 +6,7 @@ import ( + "net/http" + "testing" + +- "github.com/prebid/prebid-server/util/iputil" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + "github.com/stretchr/testify/assert" + ) + +diff --git a/util/task/ticker_task_test.go b/util/task/ticker_task_test.go +index 27551c9a..92cf6835 100644 +--- a/util/task/ticker_task_test.go ++++ b/util/task/ticker_task_test.go +@@ -4,7 +4,7 @@ import ( + "testing" + "time" + +- "github.com/prebid/prebid-server/util/task" ++ "github.com/PubMatic-OpenWrap/prebid-server/util/task" + "github.com/stretchr/testify/assert" + ) + diff --git a/docs/developers/automated-tests.md b/docs/developers/automated-tests.md index 0dff9b04212..93bd28f6187 100644 --- a/docs/developers/automated-tests.md +++ b/docs/developers/automated-tests.md @@ -15,7 +15,7 @@ For more info on how to write tests in Go, see [the Go docs](https://golang.org/ ## Adapter Tests If your adapter makes HTTP calls using standard JSON, you should use the -[RunJSONBidderTest](https://github.com/prebid/prebid-server/blob/master/adapters/adapterstest/test_json.go#L50) function. +[RunJSONBidderTest](https://github.com/PubMatic-OpenWrap/prebid-server/blob/master/adapters/adapterstest/test_json.go#L50) function. This will be much more thorough, convenient, maintainable, and reusable than writing standard Go tests for your adapter. diff --git a/docs/developers/code-reviews.md b/docs/developers/code-reviews.md index d8ee820cd80..20c824b39ba 100644 --- a/docs/developers/code-reviews.md +++ b/docs/developers/code-reviews.md @@ -1,7 +1,7 @@ # Code Reviews ## Standards -Anyone is free to review and comment on any [open pull requests](https://github.com/prebid/prebid-server/pulls). +Anyone is free to review and comment on any [open pull requests](https://github.com/PubMatic-OpenWrap/prebid-server/pulls). All pull requests must be reviewed and approved by at least one [core member](https://github.com/orgs/prebid/teams/core/members) before merge. @@ -38,7 +38,7 @@ Some examples include: - Can we improve the user's experience in any way? - Have the relevant [docs](..) been added or updated? If not, add the `needs docs` label. - Do you believe that the code works by looking at the unit tests? If not, suggest more tests until you do! -- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/prebid/prebid-server/issues) explaining it. Are there better ways to achieve those goals? +- Is the motivation behind these changes clear? If not, there must be [an issue](https://github.com/PubMatic-OpenWrap/prebid-server/issues) explaining it. Are there better ways to achieve those goals? - Does the code use any global, mutable state? [Inject dependencies](https://en.wikipedia.org/wiki/Dependency_injection) instead! - Can the code be organized into smaller, more modular pieces? - Is there dead code which can be deleted? Or TODO comments which should be resolved? diff --git a/docs/developers/contributing.md b/docs/developers/contributing.md index 2dafa67fb2e..b418efa2877 100644 --- a/docs/developers/contributing.md +++ b/docs/developers/contributing.md @@ -2,7 +2,7 @@ ## Create an issue -[Create an issue](https://github.com/prebid/prebid-server/issues/new) describing the motivation for your changes. +[Create an issue](https://github.com/PubMatic-OpenWrap/prebid-server/issues/new) describing the motivation for your changes. Are you fixing a bug? Improving documentation? Optimizing some slow code? Pull Requests without associated Issues may still be accepted, if the motivation is obvious. @@ -38,7 +38,7 @@ those updates must be submitted in the same Pull Request as the code changes. ## Open a Pull Request When you're ready, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request/) -against the `master` branch of [our GitHub repository](https://github.com/prebid/prebid-server/compare). +against the `master` branch of [our GitHub repository](https://github.com/PubMatic-OpenWrap/prebid-server/compare). Pull Requests will be vetted through [Travis CI](https://travis-ci.com/). To reproduce these same tests locally, do: @@ -49,5 +49,5 @@ To reproduce these same tests locally, do: If the tests pass locally, but fail on your PR, [update your fork](https://help.github.com/articles/syncing-a-fork/) with the latest code from `master`. -**Note**: We also have some [known intermittent failures](https://github.com/prebid/prebid-server/issues/103). +**Note**: We also have some [known intermittent failures](https://github.com/PubMatic-OpenWrap/prebid-server/issues/103). If the tests still fail after pulling `master`, don't worry about it. We'll re-run them when we review your PR. diff --git a/endpoints/auction.go b/endpoints/auction.go index 92e9769d59e..4416b602f1a 100644 --- a/endpoints/auction.go +++ b/endpoints/auction.go @@ -10,21 +10,21 @@ import ( "strconv" "time" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/cache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + gdprPrivacy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/cache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/privacy" - gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/usersync" ) type bidResult struct { diff --git a/endpoints/auction_test.go b/endpoints/auction_test.go index ed9a526d760..331ddc2500c 100644 --- a/endpoints/auction_test.go +++ b/endpoints/auction_test.go @@ -10,17 +10,17 @@ import ( "net/http/httptest" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/prebid_cache_client" - gdprPolicy "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/usersync/usersyncers" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + gdprPolicy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/usersync/usersyncers" "github.com/spf13/viper" "github.com/stretchr/testify/assert" diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index bf3935f0535..2c502a8ffb2 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -10,18 +10,18 @@ import ( "net/http" "strconv" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + gdprPrivacy "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/buger/jsonparser" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - gdprPrivacy "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/usersync" ) func NewCookieSyncEndpoint( diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index a6352387786..dc73d241bc2 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -9,18 +9,18 @@ import ( "text/template" "time" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/buger/jsonparser" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/audienceNetwork" - "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/pubmatic" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/currency_rates.go b/endpoints/currency_rates.go index d35cb74cea4..de5a47e19b9 100644 --- a/endpoints/currency_rates.go +++ b/endpoints/currency_rates.go @@ -5,8 +5,8 @@ import ( "net/http" "time" + "github.com/PubMatic-OpenWrap/prebid-server/currency" "github.com/golang/glog" - "github.com/prebid/prebid-server/currency" ) // currencyRatesInfo holds currency rates information. diff --git a/endpoints/currency_rates_test.go b/endpoints/currency_rates_test.go index 7fc513e7dbe..e73893aa837 100644 --- a/endpoints/currency_rates_test.go +++ b/endpoints/currency_rates_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/currency" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/events/account_test.go b/endpoints/events/account_test.go index ff0dd51bdf2..559b39d096c 100644 --- a/endpoints/events/account_test.go +++ b/endpoints/events/account_test.go @@ -3,10 +3,10 @@ package events import ( "errors" "fmt" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/stored_requests" "github.com/stretchr/testify/assert" "io/ioutil" "net/http" diff --git a/endpoints/events/event.go b/endpoints/events/event.go index fe178d8f271..da18b16bd53 100644 --- a/endpoints/events/event.go +++ b/endpoints/events/event.go @@ -4,12 +4,12 @@ import ( "context" "errors" "fmt" + accountService "github.com/PubMatic-OpenWrap/prebid-server/account" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/julienschmidt/httprouter" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/stored_requests" "net/http" "net/url" "strconv" diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go index ba8071843f4..d32d01ad562 100644 --- a/endpoints/events/event_test.go +++ b/endpoints/events/event_test.go @@ -4,9 +4,9 @@ import ( "context" "encoding/base64" "encoding/json" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/stretchr/testify/assert" "io/ioutil" "net/http" diff --git a/endpoints/events/vtrack.go b/endpoints/events/vtrack.go index 9a4d9c089b3..90e597ab7fc 100644 --- a/endpoints/events/vtrack.go +++ b/endpoints/events/vtrack.go @@ -10,15 +10,15 @@ import ( "strings" "time" + accountService "github.com/PubMatic-OpenWrap/prebid-server/account" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" ) const ( diff --git a/endpoints/events/vtrack_test.go b/endpoints/events/vtrack_test.go index 0ec04663d01..52665e7736d 100644 --- a/endpoints/events/vtrack_test.go +++ b/endpoints/events/vtrack_test.go @@ -5,10 +5,10 @@ import ( "context" "encoding/json" "fmt" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/stretchr/testify/assert" "io/ioutil" "net/http/httptest" diff --git a/endpoints/getuids.go b/endpoints/getuids.go index 859c0e7288c..e02efe15b3a 100644 --- a/endpoints/getuids.go +++ b/endpoints/getuids.go @@ -3,9 +3,9 @@ package endpoints import ( "net/http" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/usersync" "encoding/json" ) diff --git a/endpoints/getuids_test.go b/endpoints/getuids_test.go index 7988acbaffe..fb984e15c35 100644 --- a/endpoints/getuids_test.go +++ b/endpoints/getuids_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "testing" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/info/bidders.go b/endpoints/info/bidders.go index 44482106786..7c44c7ec6fa 100644 --- a/endpoints/info/bidders.go +++ b/endpoints/info/bidders.go @@ -4,11 +4,11 @@ import ( "encoding/json" "net/http" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/buger/jsonparser" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" ) // NewBiddersEndpoint implements /info/bidders diff --git a/endpoints/info/bidders_test.go b/endpoints/info/bidders_test.go index 8229472b84f..15e5f77de23 100644 --- a/endpoints/info/bidders_test.go +++ b/endpoints/info/bidders_test.go @@ -14,10 +14,10 @@ import ( "github.com/julienschmidt/httprouter" "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints/info" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints/info" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" yaml "gopkg.in/yaml.v2" ) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index dc66163a699..f6cef770694 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -11,25 +11,25 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/openrtb" + accountService "github.com/PubMatic-OpenWrap/prebid-server/account" + "github.com/PubMatic-OpenWrap/prebid-server/amp" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" "github.com/buger/jsonparser" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/mxmCherry/openrtb" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/amp" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/iputil" ) const defaultAmpRequestTimeoutMillis = 900 diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 97e99c6bd01..cbff32413ba 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -11,15 +11,15 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - - "github.com/mxmCherry/openrtb" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + + "github.com/PubMatic-OpenWrap/openrtb" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" gometrics "github.com/rcrowley/go-metrics" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 04e0c2cff95..582be4aa494 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -13,28 +13,28 @@ import ( "strconv" "time" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/openrtb/native" + nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" + accountService "github.com/PubMatic-OpenWrap/prebid-server/account" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/util/httputil" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" "github.com/gofrs/uuid" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/mxmCherry/openrtb" - "github.com/mxmCherry/openrtb/native" - nativeRequests "github.com/mxmCherry/openrtb/native/request" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/httputil" - "github.com/prebid/prebid-server/util/iputil" "golang.org/x/net/publicsuffix" ) @@ -831,7 +831,7 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb.Imp, aliases map[string]st // NOTE: This is not part of the official API yet, so we are not expecting clients // to migrate from imp[...].ext.${BIDDER} to imp[...].ext.prebid.bidder.${BIDDER} // at this time - // https://github.com/prebid/prebid-server/pull/846#issuecomment-476352224 + // https://github.com/PubMatic-OpenWrap/prebid-server/pull/846#issuecomment-476352224 if rawPrebidExt, ok := bidderExts[openrtb_ext.PrebidExtKey]; ok { var prebidExt openrtb_ext.ExtImpPrebid if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil { diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go index b42e45b68d9..89965db0430 100644 --- a/endpoints/openrtb2/auction_benchmark_test.go +++ b/endpoints/openrtb2/auction_benchmark_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/currency" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" ) // dummyServer returns the header bidding test ad. This response was scraped from a real appnexus server response. diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 413d7995c55..7e67770173c 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -17,20 +17,20 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/stored_requests" - + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/PubMatic-OpenWrap/openrtb" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" - "github.com/mxmCherry/openrtb" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/util/iputil" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/interstitial.go b/endpoints/openrtb2/interstitial.go index b42945488cc..2f9c48fcd79 100644 --- a/endpoints/openrtb2/interstitial.go +++ b/endpoints/openrtb2/interstitial.go @@ -4,10 +4,10 @@ import ( "encoding/json" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func processInterstitials(req *openrtb.BidRequest) error { diff --git a/endpoints/openrtb2/interstitial_test.go b/endpoints/openrtb2/interstitial_test.go index 1c6eb2555db..93d310525c8 100644 --- a/endpoints/openrtb2/interstitial_test.go +++ b/endpoints/openrtb2/interstitial_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index 7735d886730..1c1846a7c9d 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -14,24 +14,24 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" "github.com/gofrs/uuid" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/util/iputil" + "github.com/PubMatic-OpenWrap/openrtb" + accountService "github.com/PubMatic-OpenWrap/prebid-server/account" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/mxmCherry/openrtb" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" ) var defaultRequestTimeout int64 = 5000 diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index a70d45ac3b8..9bcf6522ec6 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -12,15 +12,15 @@ import ( "strings" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/analytics" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 4bff02acf37..b6832951625 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -9,13 +9,13 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" ) const ( diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index fc98608ef9f..2b46fddecd0 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -10,17 +10,17 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - metricsConf "github.com/prebid/prebid-server/metrics/config" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" ) func TestSetUIDEndpoint(t *testing.T) { diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index f05a4d817fe..2bc3a99a4ee 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -1,102 +1,102 @@ package exchange import ( - "github.com/prebid/prebid-server/adapters" - ttx "github.com/prebid/prebid-server/adapters/33across" - "github.com/prebid/prebid-server/adapters/acuityads" - "github.com/prebid/prebid-server/adapters/adform" - "github.com/prebid/prebid-server/adapters/adgeneration" - "github.com/prebid/prebid-server/adapters/adhese" - "github.com/prebid/prebid-server/adapters/adkernel" - "github.com/prebid/prebid-server/adapters/adkernelAdn" - "github.com/prebid/prebid-server/adapters/adman" - "github.com/prebid/prebid-server/adapters/admixer" - "github.com/prebid/prebid-server/adapters/adocean" - "github.com/prebid/prebid-server/adapters/adoppler" - "github.com/prebid/prebid-server/adapters/adot" - "github.com/prebid/prebid-server/adapters/adpone" - "github.com/prebid/prebid-server/adapters/adprime" - "github.com/prebid/prebid-server/adapters/adtarget" - "github.com/prebid/prebid-server/adapters/adtelligent" - "github.com/prebid/prebid-server/adapters/advangelists" - "github.com/prebid/prebid-server/adapters/aja" - "github.com/prebid/prebid-server/adapters/amx" - "github.com/prebid/prebid-server/adapters/applogy" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/audienceNetwork" - "github.com/prebid/prebid-server/adapters/avocet" - "github.com/prebid/prebid-server/adapters/beachfront" - "github.com/prebid/prebid-server/adapters/beintoo" - "github.com/prebid/prebid-server/adapters/between" - "github.com/prebid/prebid-server/adapters/brightroll" - "github.com/prebid/prebid-server/adapters/colossus" - "github.com/prebid/prebid-server/adapters/connectad" - "github.com/prebid/prebid-server/adapters/consumable" - "github.com/prebid/prebid-server/adapters/conversant" - "github.com/prebid/prebid-server/adapters/cpmstar" - "github.com/prebid/prebid-server/adapters/datablocks" - "github.com/prebid/prebid-server/adapters/decenterads" - "github.com/prebid/prebid-server/adapters/deepintent" - "github.com/prebid/prebid-server/adapters/dmx" - "github.com/prebid/prebid-server/adapters/emx_digital" - "github.com/prebid/prebid-server/adapters/engagebdr" - "github.com/prebid/prebid-server/adapters/eplanning" - "github.com/prebid/prebid-server/adapters/gamma" - "github.com/prebid/prebid-server/adapters/gamoshi" - "github.com/prebid/prebid-server/adapters/grid" - "github.com/prebid/prebid-server/adapters/gumgum" - "github.com/prebid/prebid-server/adapters/improvedigital" - "github.com/prebid/prebid-server/adapters/inmobi" - "github.com/prebid/prebid-server/adapters/invibes" - "github.com/prebid/prebid-server/adapters/ix" - "github.com/prebid/prebid-server/adapters/kidoz" - "github.com/prebid/prebid-server/adapters/krushmedia" - "github.com/prebid/prebid-server/adapters/kubient" - "github.com/prebid/prebid-server/adapters/lockerdome" - "github.com/prebid/prebid-server/adapters/logicad" - "github.com/prebid/prebid-server/adapters/lunamedia" - "github.com/prebid/prebid-server/adapters/marsmedia" - "github.com/prebid/prebid-server/adapters/mgid" - "github.com/prebid/prebid-server/adapters/mobfoxpb" - "github.com/prebid/prebid-server/adapters/mobilefuse" - "github.com/prebid/prebid-server/adapters/nanointeractive" - "github.com/prebid/prebid-server/adapters/ninthdecimal" - "github.com/prebid/prebid-server/adapters/nobid" - "github.com/prebid/prebid-server/adapters/openx" - "github.com/prebid/prebid-server/adapters/orbidder" - "github.com/prebid/prebid-server/adapters/pubmatic" - "github.com/prebid/prebid-server/adapters/pubnative" - "github.com/prebid/prebid-server/adapters/pulsepoint" - "github.com/prebid/prebid-server/adapters/revcontent" - "github.com/prebid/prebid-server/adapters/rhythmone" - "github.com/prebid/prebid-server/adapters/rtbhouse" - "github.com/prebid/prebid-server/adapters/rubicon" - "github.com/prebid/prebid-server/adapters/sharethrough" - "github.com/prebid/prebid-server/adapters/silvermob" - "github.com/prebid/prebid-server/adapters/smaato" - "github.com/prebid/prebid-server/adapters/smartadserver" - "github.com/prebid/prebid-server/adapters/smartrtb" - "github.com/prebid/prebid-server/adapters/smartyads" - "github.com/prebid/prebid-server/adapters/somoaudience" - "github.com/prebid/prebid-server/adapters/sonobi" - "github.com/prebid/prebid-server/adapters/sovrn" - "github.com/prebid/prebid-server/adapters/synacormedia" - "github.com/prebid/prebid-server/adapters/tappx" - "github.com/prebid/prebid-server/adapters/telaria" - "github.com/prebid/prebid-server/adapters/triplelift" - "github.com/prebid/prebid-server/adapters/triplelift_native" - "github.com/prebid/prebid-server/adapters/ucfunnel" - "github.com/prebid/prebid-server/adapters/unruly" - "github.com/prebid/prebid-server/adapters/valueimpression" - "github.com/prebid/prebid-server/adapters/verizonmedia" - "github.com/prebid/prebid-server/adapters/visx" - "github.com/prebid/prebid-server/adapters/vrtcal" - "github.com/prebid/prebid-server/adapters/yeahmobi" - "github.com/prebid/prebid-server/adapters/yieldlab" - "github.com/prebid/prebid-server/adapters/yieldmo" - "github.com/prebid/prebid-server/adapters/yieldone" - "github.com/prebid/prebid-server/adapters/zeroclickfraud" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + ttx "github.com/PubMatic-OpenWrap/prebid-server/adapters/33across" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/acuityads" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adgeneration" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adhese" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernel" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernelAdn" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adman" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/admixer" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adocean" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adoppler" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adot" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adpone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adprime" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtarget" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtelligent" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/advangelists" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/aja" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/amx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/applogy" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/avocet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/beachfront" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/beintoo" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/between" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/brightroll" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/colossus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/connectad" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/consumable" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/cpmstar" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/datablocks" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/decenterads" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/deepintent" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/dmx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/emx_digital" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/engagebdr" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/eplanning" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamma" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamoshi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/grid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gumgum" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/improvedigital" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/inmobi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/invibes" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/kidoz" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/krushmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/kubient" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lockerdome" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/logicad" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lunamedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/marsmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/mgid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/mobfoxpb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/mobilefuse" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/nanointeractive" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ninthdecimal" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/nobid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/openx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/orbidder" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubnative" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/revcontent" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rhythmone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rtbhouse" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sharethrough" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/silvermob" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smaato" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartadserver" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartyads" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/somoaudience" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sonobi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/synacormedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/tappx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/telaria" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift_native" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ucfunnel" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/unruly" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/valueimpression" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/verizonmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/visx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/vrtcal" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yeahmobi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldlab" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldmo" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/zeroclickfraud" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Adapter registration is kept in this separate file for ease of use and to aid diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go index 7e271376868..4cd9c6ddafd 100644 --- a/exchange/adapter_util.go +++ b/exchange/adapter_util.go @@ -4,12 +4,12 @@ import ( "fmt" "net/http" - "github.com/prebid/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func BuildAdapters(client *http.Client, cfg *config.Configuration, infos adapters.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]adaptedBidder, []error) { diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go index 92eb01291fb..05cdd7e9648 100644 --- a/exchange/adapter_util_test.go +++ b/exchange/adapter_util_test.go @@ -6,15 +6,15 @@ import ( "net/http" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/rubicon" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - metrics "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + metrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/auction.go b/exchange/auction.go index 97f82d3c6eb..b8143d4e1a8 100644 --- a/exchange/auction.go +++ b/exchange/auction.go @@ -10,11 +10,11 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" uuid "github.com/gofrs/uuid" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" ) type DebugLog struct { diff --git a/exchange/auction_test.go b/exchange/auction_test.go index 7e8a8a8cec9..ba9f12077c9 100644 --- a/exchange/auction_test.go +++ b/exchange/auction_test.go @@ -11,11 +11,11 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/exchange/bidder.go b/exchange/bidder.go index 5b5852bdd62..18a42a7c023 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -12,18 +12,18 @@ import ( "net/http/httptrace" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config/util" + "github.com/PubMatic-OpenWrap/prebid-server/currency" "github.com/golang/glog" - "github.com/prebid/prebid-server/config/util" - "github.com/prebid/prebid-server/currency" - - "github.com/mxmCherry/openrtb" - nativeRequests "github.com/mxmCherry/openrtb/native/request" - nativeResponse "github.com/mxmCherry/openrtb/native/response" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + + "github.com/PubMatic-OpenWrap/openrtb" + nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" + nativeResponse "github.com/PubMatic-OpenWrap/openrtb/native/response" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "golang.org/x/net/context/ctxhttp" ) diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index b809c7e0f51..66f976c0b66 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -15,19 +15,19 @@ import ( "testing" "time" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - nativeRequests "github.com/mxmCherry/openrtb/native/request" - nativeResponse "github.com/mxmCherry/openrtb/native/response" + nativeRequests "github.com/PubMatic-OpenWrap/openrtb/native/request" + nativeResponse "github.com/PubMatic-OpenWrap/openrtb/native/response" ) // TestSingleBidder makes sure that the following things work if the Bidder needs only one request. diff --git a/exchange/bidder_validate_bids.go b/exchange/bidder_validate_bids.go index cf74dfb1dd5..9ea357336fa 100644 --- a/exchange/bidder_validate_bids.go +++ b/exchange/bidder_validate_bids.go @@ -6,10 +6,10 @@ import ( "fmt" "strings" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" goCurrency "golang.org/x/text/currency" ) diff --git a/exchange/bidder_validate_bids_test.go b/exchange/bidder_validate_bids_test.go index e7fc0b046dd..a2ec026f5d7 100644 --- a/exchange/bidder_validate_bids_test.go +++ b/exchange/bidder_validate_bids_test.go @@ -4,10 +4,10 @@ import ( "context" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/events.go b/exchange/events.go index 3a05e098c39..99b494839df 100644 --- a/exchange/events.go +++ b/exchange/events.go @@ -4,13 +4,13 @@ import ( "encoding/json" "time" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints/events" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" jsonpatch "github.com/evanphx/json-patch" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints/events" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" ) // eventTracking has configuration fields needed for adding event tracking to an auction response diff --git a/exchange/events_test.go b/exchange/events_test.go index 1d72739d873..3315dfaae7b 100644 --- a/exchange/events_test.go +++ b/exchange/events_test.go @@ -3,8 +3,8 @@ package exchange import ( "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/exchange.go b/exchange/exchange.go index cfb9c6d39da..2358763a155 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -14,18 +14,18 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/stored_requests" - + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" ) type ContextKey string diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 8bdf6c451e0..3eb8fff7a78 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -15,20 +15,20 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" - + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/file_fetcher" + + "github.com/PubMatic-OpenWrap/openrtb" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" "github.com/stretchr/testify/assert" "github.com/yudai/gojsondiff" "github.com/yudai/gojsondiff/formatter" @@ -74,15 +74,15 @@ func TestNewExchange(t *testing.T) { // and check whether the returned request successfully prints any '&' characters as it should // To do so, we: // 1) Write the endpoint adapter URL with an '&' character into a new config,Configuration struct -// as specified in https://github.com/prebid/prebid-server/issues/465 +// as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 // 2) Initialize a new exchange with said configuration // 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs including the -// sample request as specified in https://github.com/prebid/prebid-server/issues/465 +// sample request as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 // 4) Build a BidResponse struct using exchange.buildBidResponse(ctx.Background(), liveA... ) // 5) Assert we have no '&' characters in the response that exchange.buildBidResponse returns func TestCharacterEscape(t *testing.T) { /* 1) Adapter with a '& char in its endpoint property */ - /* https://github.com/prebid/prebid-server/issues/465 */ + /* https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 */ cfg := &config.Configuration{ Adapters: make(map[string]config.Adapter, 1), } @@ -114,7 +114,7 @@ func TestCharacterEscape(t *testing.T) { adapterBids := make(map[openrtb_ext.BidderName]*pbsOrtbSeatBid, 1) adapterBids["appnexus"] = &pbsOrtbSeatBid{currency: "USD"} - //An openrtb.BidRequest struct as specified in https://github.com/prebid/prebid-server/issues/465 + //An openrtb.BidRequest struct as specified in https://github.com/PubMatic-OpenWrap/prebid-server/issues/465 bidRequest := &openrtb.BidRequest{ ID: "some-request-id", Imp: []openrtb.Imp{{ diff --git a/exchange/gdpr.go b/exchange/gdpr.go index e861f948773..6b6a073bb23 100644 --- a/exchange/gdpr.go +++ b/exchange/gdpr.go @@ -3,8 +3,8 @@ package exchange import ( "encoding/json" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" ) // ExtractGDPR will pull the gdpr flag from an openrtb request diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go index 351939091da..e767c269b3b 100644 --- a/exchange/gdpr_test.go +++ b/exchange/gdpr_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" "github.com/stretchr/testify/assert" ) diff --git a/exchange/legacy.go b/exchange/legacy.go index b4845b76c69..9b80da00089 100644 --- a/exchange/legacy.go +++ b/exchange/legacy.go @@ -5,13 +5,13 @@ import ( "encoding/json" "errors" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" ) // AdaptLegacyAdapter turns a bidder.Adapter into an adaptedBidder. diff --git a/exchange/legacy_test.go b/exchange/legacy_test.go index 2cef4feae40..2474fda50e1 100644 --- a/exchange/legacy_test.go +++ b/exchange/legacy_test.go @@ -9,14 +9,14 @@ import ( "testing" "time" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/buger/jsonparser" jsonpatch "github.com/evanphx/json-patch" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - "github.com/prebid/prebid-server/usersync" ) func TestSiteVideo(t *testing.T) { diff --git a/exchange/price_granularity.go b/exchange/price_granularity.go index 242d420f1fc..ffcce061465 100644 --- a/exchange/price_granularity.go +++ b/exchange/price_granularity.go @@ -4,7 +4,7 @@ import ( "math" "strconv" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // GetPriceBucket is the externally facing function for computing CPM buckets diff --git a/exchange/price_granularity_test.go b/exchange/price_granularity_test.go index 13840838ba7..6dccc677b7b 100644 --- a/exchange/price_granularity_test.go +++ b/exchange/price_granularity_test.go @@ -4,7 +4,7 @@ import ( "math" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/targeting.go b/exchange/targeting.go index 87bec6f3077..6131f055ae2 100644 --- a/exchange/targeting.go +++ b/exchange/targeting.go @@ -3,8 +3,8 @@ package exchange import ( "strconv" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const MaxKeyLength = 20 diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index 2a77c4f7517..e6db921529c 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -8,17 +8,17 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" - "github.com/prebid/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" - metricsConf "github.com/prebid/prebid-server/metrics/config" - metricsConfig "github.com/prebid/prebid-server/metrics/config" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + metricsConfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/utils.go b/exchange/utils.go index 8d4c0facd3b..054213fa361 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -8,15 +8,15 @@ import ( "github.com/prebid/go-gdpr/vendorconsent" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/privacy" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/lmt" "github.com/buger/jsonparser" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/lmt" ) var integrationTypeMap = map[metrics.RequestType]config.IntegrationType{ diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 0407b3c5e0e..e222103e44c 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -7,12 +7,12 @@ import ( "fmt" "testing" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index f24fd6c56f5..272bc1f21f0 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -5,10 +5,10 @@ import ( "net/http" "strconv" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/prebid/go-gdpr/vendorlist" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" ) type Permissions interface { diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go index 5048cf118f5..81d3c6156f7 100644 --- a/gdpr/gdpr_test.go +++ b/gdpr/gdpr_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/gdpr/impl.go b/gdpr/impl.go index 06b625da95c..8d9a357393d 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -4,18 +4,18 @@ import ( "context" "fmt" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/prebid/go-gdpr/api" tcf1constants "github.com/prebid/go-gdpr/consentconstants" consentconstants "github.com/prebid/go-gdpr/consentconstants/tcf2" "github.com/prebid/go-gdpr/vendorconsent" tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" "github.com/prebid/go-gdpr/vendorlist" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" ) // This file implements GDPR permissions for the app. -// For more info, see https://github.com/prebid/prebid-server/issues/501 +// For more info, see https://github.com/PubMatic-OpenWrap/prebid-server/issues/501 // // Nothing in this file is exported. Public APIs can be found in gdpr.go diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index 45d2ba43ce3..1977c4cf62e 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -6,8 +6,8 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/go-gdpr/vendorlist2" diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index 95a17109c90..61fa166d212 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -10,11 +10,11 @@ import ( "sync/atomic" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/golang/glog" "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/go-gdpr/vendorlist2" - "github.com/prebid/prebid-server/config" "golang.org/x/net/context/ctxhttp" ) @@ -22,7 +22,7 @@ type saveVendors func(uint16, api.VendorList) // This file provides the vendorlist-fetching function for Prebid Server. // -// For more info, see https://github.com/prebid/prebid-server/issues/504 +// For more info, see https://github.com/PubMatic-OpenWrap/prebid-server/issues/504 // // Nothing in this file is exported. Public APIs can be found in gdpr.go diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index 77f3f29463c..cd97b300883 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" ) func TestTCF1FetcherInitialLoad(t *testing.T) { diff --git a/go.mod b/go.mod index 48fc6b6479b..78444e342ac 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/prebid/prebid-server +module github.com/PubMatic-OpenWrap/prebid-server go 1.14 @@ -7,6 +7,7 @@ require ( github.com/DATA-DOG/go-sqlmock v1.3.0 github.com/NYTimes/gziphandler v1.1.1 github.com/OneOfOne/xxhash v1.2.5 // indirect + github.com/PubMatic-OpenWrap/openrtb v11.0.1-0.20200228131822-5216ebe65c0c+incompatible github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/blang/semver v3.5.1+incompatible @@ -23,19 +24,15 @@ require ( github.com/influxdata/influxdb v1.6.1 github.com/julienschmidt/httprouter v1.1.0 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect - github.com/kr/pretty v0.2.0 // indirect github.com/lib/pq v1.0.0 github.com/magiconair/properties v1.8.0 github.com/mattn/go-colorable v0.1.2 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.0.0 // indirect - github.com/mssola/user_agent v0.4.1 // indirect - github.com/mxmCherry/openrtb v11.0.0+incompatible github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect github.com/prebid/go-gdpr v0.8.3 - github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf // indirect github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect diff --git a/go.sum b/go.sum index 6da3f8898ba..dc821b1d491 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PubMatic-OpenWrap/openrtb v11.0.1-0.20200228131822-5216ebe65c0c+incompatible h1:BGwndVLu0ncwweHnofXzLo+SnRMe04Bq3KFfELLzif4= +github.com/PubMatic-OpenWrap/openrtb v11.0.1-0.20200228131822-5216ebe65c0c+incompatible/go.mod h1:Ply/+GFe6FLkPMLV8Yh8xW0MpqclQyVf7m4PRsnaLDY= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= @@ -48,11 +50,6 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -65,10 +62,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mssola/user_agent v0.4.1 h1:iTUaMpVrb2qWyvUw8UvK3ygWMd2lB1NGuZ1xhpBf1eg= -github.com/mssola/user_agent v0.4.1/go.mod h1:UFiKPVaShrJGW93n4uo8dpPdg1BSVpw2P9bneo0Mtp8= -github.com/mxmCherry/openrtb v11.0.0+incompatible h1:tNzh7vKwQ8lopBAadyN3QPryawXSaVXYWi1IVluXHiM= -github.com/mxmCherry/openrtb v11.0.0+incompatible/go.mod h1:zpnz6Au3bzTGplpRU0kvFPNT6g4ROAKx/GkrslFDwZk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -80,12 +73,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prebid/go-gdpr v0.8.3 h1:rjCZNV0AdKygiGHpVhNB42usjEpTN3qidXUPB1yarb0= github.com/prebid/go-gdpr v0.8.3/go.mod h1:TGzgqQDGKOVUkbqmY25K4uvcwMywSddXEaY4zUFiVBQ= -github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf h1:CcE+KN1tCtWKsUFH5IzdQxHIgP609VSIVe5Hywg2phs= -github.com/prebid/prebid-cache v0.0.0-20200218152159-6d6d678c1caf/go.mod h1:k5xrl5ZpnumN1S2x8w8cMiFYsgRuVyAeFJz+BkSi+98= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed h1:0dloFFFNNDG7c+8qtkYw2FdADrWy9s5cI8wHp6tK3Mg= github.com/prometheus/client_golang v0.0.0-20180623155954-77e8f2ddcfed/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A= -github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54= @@ -136,7 +125,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/main.go b/main.go index 2ae75a2e8cb..bca61563b7f 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,12 @@ import ( "net/http" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/router" - "github.com/prebid/prebid-server/server" - "github.com/prebid/prebid-server/util/task" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/router" + "github.com/PubMatic-OpenWrap/prebid-server/server" + "github.com/PubMatic-OpenWrap/prebid-server/util/task" "github.com/golang/glog" "github.com/spf13/viper" diff --git a/main_test.go b/main_test.go index 70eea2825f0..d4f96a833e7 100644 --- a/main_test.go +++ b/main_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/stretchr/testify/assert" "github.com/spf13/viper" diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go index c1f726e904e..c828c28fb22 100644 --- a/metrics/config/metrics.go +++ b/metrics/config/metrics.go @@ -3,10 +3,10 @@ package config import ( "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - prometheusmetrics "github.com/prebid/prebid-server/metrics/prometheus" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + prometheusmetrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/prometheus" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" gometrics "github.com/rcrowley/go-metrics" influxdb "github.com/vrischmann/go-metrics-influxdb" ) diff --git a/metrics/config/metrics_test.go b/metrics/config/metrics_test.go index 5b70b53bb1a..5465061d5a0 100644 --- a/metrics/config/metrics_test.go +++ b/metrics/config/metrics_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - mainConfig "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + mainConfig "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" gometrics "github.com/rcrowley/go-metrics" ) diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go index ac7ed0691c4..a98798970ba 100644 --- a/metrics/go_metrics.go +++ b/metrics/go_metrics.go @@ -5,9 +5,9 @@ import ( "sync" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" metrics "github.com/rcrowley/go-metrics" ) diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go index 2d0b9097b11..712e8d5254c 100644 --- a/metrics/go_metrics_test.go +++ b/metrics/go_metrics_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" metrics "github.com/rcrowley/go-metrics" "github.com/stretchr/testify/assert" ) diff --git a/metrics/metrics.go b/metrics/metrics.go index 62de3afac21..4840e67d056 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -3,7 +3,7 @@ package metrics import ( "time" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Labels defines the labels that can be attached to the metrics. diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go index c852eb47d24..62979d6db1d 100644 --- a/metrics/metrics_mock.go +++ b/metrics/metrics_mock.go @@ -3,7 +3,7 @@ package metrics import ( "time" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/mock" ) diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go index f4dfe43469d..0f2ada29e3a 100644 --- a/metrics/prometheus/preload.go +++ b/metrics/prometheus/preload.go @@ -1,7 +1,7 @@ package prometheusmetrics import ( - "github.com/prebid/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" "github.com/prometheus/client_golang/prometheus" ) diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 1e384c0e438..a43408ced08 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -4,9 +4,9 @@ import ( "strconv" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/prometheus/client_golang/prometheus" ) diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index b2ffff34850..5f76a1826a7 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" diff --git a/metrics/prometheus/type_conversion.go b/metrics/prometheus/type_conversion.go index 0e5c80636db..eff379e8b2c 100644 --- a/metrics/prometheus/type_conversion.go +++ b/metrics/prometheus/type_conversion.go @@ -3,8 +3,8 @@ package prometheusmetrics import ( "strconv" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func actionsAsString() []string { diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go index 09edf51d0db..13ec8eb4538 100644 --- a/openrtb_ext/bid_request_video.go +++ b/openrtb_ext/bid_request_video.go @@ -1,7 +1,7 @@ package openrtb_ext import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) type BidRequestVideo struct { diff --git a/openrtb_ext/deal_tier.go b/openrtb_ext/deal_tier.go index 8583c053e8f..e882235d01e 100644 --- a/openrtb_ext/deal_tier.go +++ b/openrtb_ext/deal_tier.go @@ -3,7 +3,7 @@ package openrtb_ext import ( "encoding/json" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // DealTier defines the configuration of a deal tier. diff --git a/openrtb_ext/deal_tier_test.go b/openrtb_ext/deal_tier_test.go index 1bb4d5648f0..717e0703466 100644 --- a/openrtb_ext/deal_tier_test.go +++ b/openrtb_ext/deal_tier_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/openrtb_ext/device.go b/openrtb_ext/device.go index b3fd6cbb48f..afbea276988 100644 --- a/openrtb_ext/device.go +++ b/openrtb_ext/device.go @@ -3,8 +3,8 @@ package openrtb_ext import ( "strconv" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/errortypes" ) // PrebidExtKey represents the prebid extension key used in requests diff --git a/openrtb_ext/device_test.go b/openrtb_ext/device_test.go index e307374ef38..b4c85bcc0b0 100644 --- a/openrtb_ext/device_test.go +++ b/openrtb_ext/device_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go index 02370d19376..f2119f2c871 100644 --- a/openrtb_ext/response.go +++ b/openrtb_ext/response.go @@ -1,7 +1,7 @@ package openrtb_ext import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // ExtBidResponse defines the contract for bidresponse.ext diff --git a/openrtb_ext/site_test.go b/openrtb_ext/site_test.go index 67ec6cc4f99..0d41e0c02ce 100644 --- a/openrtb_ext/site_test.go +++ b/openrtb_ext/site_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/pbs/pbsrequest.go b/pbs/pbsrequest.go index 30f8bd25c0d..b3e20af7ce9 100644 --- a/pbs/pbsrequest.go +++ b/pbs/pbsrequest.go @@ -10,17 +10,17 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/cache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/httputil" - "github.com/prebid/prebid-server/util/iputil" - + "github.com/PubMatic-OpenWrap/prebid-server/cache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" + "github.com/PubMatic-OpenWrap/prebid-server/util/httputil" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" + + "github.com/PubMatic-OpenWrap/openrtb" "github.com/blang/semver" "github.com/buger/jsonparser" "github.com/golang/glog" - "github.com/mxmCherry/openrtb" "golang.org/x/net/publicsuffix" ) diff --git a/pbs/pbsrequest_test.go b/pbs/pbsrequest_test.go index 52cd6153323..566057473b8 100644 --- a/pbs/pbsrequest_test.go +++ b/pbs/pbsrequest_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/config" "github.com/magiconair/properties/assert" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/config" ) const mimeVideoMp4 = "video/mp4" diff --git a/pbs/usersync.go b/pbs/usersync.go index 4cac3544804..f50933b2434 100644 --- a/pbs/usersync.go +++ b/pbs/usersync.go @@ -9,13 +9,13 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/prebid-server/analytics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/server/ssl" - "github.com/prebid/prebid-server/usersync" ) // Recaptcha code from https://github.com/haisum/recaptcha/blob/master/recaptcha.go diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go index 88c197cd0b8..8d363e147bd 100644 --- a/prebid_cache_client/client.go +++ b/prebid_cache_client/client.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" "github.com/buger/jsonparser" "github.com/golang/glog" diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go index 1ba30a6faab..c4eeca82b8f 100644 --- a/prebid_cache_client/client_test.go +++ b/prebid_cache_client/client_test.go @@ -10,9 +10,9 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/privacy/ccpa/consentwriter.go b/privacy/ccpa/consentwriter.go index 4856655402b..4ef412fd3ef 100644 --- a/privacy/ccpa/consentwriter.go +++ b/privacy/ccpa/consentwriter.go @@ -1,7 +1,7 @@ package ccpa import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // ConsentWriter implements the PolicyWriter interface for CCPA. diff --git a/privacy/ccpa/consentwriter_test.go b/privacy/ccpa/consentwriter_test.go index 57a7f8f4ddf..1e491d9d167 100644 --- a/privacy/ccpa/consentwriter_test.go +++ b/privacy/ccpa/consentwriter_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/privacy/ccpa/parsedpolicy.go b/privacy/ccpa/parsedpolicy.go index 3c934e67822..52977104716 100644 --- a/privacy/ccpa/parsedpolicy.go +++ b/privacy/ccpa/parsedpolicy.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/prebid/prebid-server/errortypes" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" ) const ( diff --git a/privacy/ccpa/parsedpolicy_test.go b/privacy/ccpa/parsedpolicy_test.go index 2f7e8bfd683..4fa9f92684d 100644 --- a/privacy/ccpa/parsedpolicy_test.go +++ b/privacy/ccpa/parsedpolicy_test.go @@ -3,7 +3,7 @@ package ccpa import ( "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go index a9f1c49e47d..3f5dd25c6bc 100644 --- a/privacy/ccpa/policy.go +++ b/privacy/ccpa/policy.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "github.com/mxmCherry/openrtb" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/openrtb" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) // Policy represents the CCPA regulatory information from an OpenRTB bid request. diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go index 7ff896e9ebf..c1fdd9cd903 100644 --- a/privacy/ccpa/policy_test.go +++ b/privacy/ccpa/policy_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/privacy/enforcement.go b/privacy/enforcement.go index 64070ae3a6a..47c1f0d03dd 100644 --- a/privacy/enforcement.go +++ b/privacy/enforcement.go @@ -1,7 +1,7 @@ package privacy import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // Enforcement represents the privacy policies to enforce for an OpenRTB bid request. diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go index 9240aafc2c3..a5e41c83198 100644 --- a/privacy/enforcement_test.go +++ b/privacy/enforcement_test.go @@ -3,7 +3,7 @@ package privacy import ( "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/privacy/gdpr/consentwriter.go b/privacy/gdpr/consentwriter.go index 040bbd6c94b..f1cc2ce12f7 100644 --- a/privacy/gdpr/consentwriter.go +++ b/privacy/gdpr/consentwriter.go @@ -3,9 +3,9 @@ package gdpr import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // ConsentWriter implements the PolicyWriter interface for GDPR TCF. diff --git a/privacy/gdpr/consentwriter_test.go b/privacy/gdpr/consentwriter_test.go index 77fbdf88d92..65df8051d02 100644 --- a/privacy/gdpr/consentwriter_test.go +++ b/privacy/gdpr/consentwriter_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/privacy/lmt/policy.go b/privacy/lmt/policy.go index 295dcc46469..5f23b9a3eef 100644 --- a/privacy/lmt/policy.go +++ b/privacy/lmt/policy.go @@ -1,7 +1,7 @@ package lmt import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) const ( diff --git a/privacy/lmt/policy_test.go b/privacy/lmt/policy_test.go index 3027414fd02..9d0e3b6aa9a 100644 --- a/privacy/lmt/policy_test.go +++ b/privacy/lmt/policy_test.go @@ -3,7 +3,7 @@ package lmt import ( "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/privacy/policies.go b/privacy/policies.go index bc844a4e463..a1c3fca49be 100644 --- a/privacy/policies.go +++ b/privacy/policies.go @@ -1,9 +1,9 @@ package privacy import ( - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" - "github.com/prebid/prebid-server/privacy/lmt" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/ccpa" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/gdpr" + "github.com/PubMatic-OpenWrap/prebid-server/privacy/lmt" ) // Policies represents the privacy regulations for an OpenRTB bid request. diff --git a/privacy/scrubber.go b/privacy/scrubber.go index 8771c8b3282..aea5c9008f4 100644 --- a/privacy/scrubber.go +++ b/privacy/scrubber.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strings" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // ScrubStrategyIPV4 defines the approach to scrub PII from an IPV4 address. diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go index e0a2cb86f64..4d989e1c5a1 100644 --- a/privacy/scrubber_test.go +++ b/privacy/scrubber_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/privacy/writer.go b/privacy/writer.go index c61767f16c8..a68a158ced8 100644 --- a/privacy/writer.go +++ b/privacy/writer.go @@ -1,7 +1,7 @@ package privacy import ( - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" ) // PolicyWriter mutates an OpenRTB bid request with a policy's regulatory information. diff --git a/privacy/writer_test.go b/privacy/writer_test.go index 79170cfc451..754e6ffe2c9 100644 --- a/privacy/writer_test.go +++ b/privacy/writer_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/mxmCherry/openrtb" + "github.com/PubMatic-OpenWrap/openrtb" "github.com/stretchr/testify/assert" ) diff --git a/router/admin.go b/router/admin.go index b66bf55a5d6..ba4f25f585b 100644 --- a/router/admin.go +++ b/router/admin.go @@ -5,8 +5,8 @@ import ( "net/http/pprof" "time" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/endpoints" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints" ) func Admin(revision string, rateConverter *currency.RateConverter, rateConverterFetchingInterval time.Duration) *http.ServeMux { diff --git a/router/aspects/request_timeout_handler.go b/router/aspects/request_timeout_handler.go index 39a4341f995..9c51b9b8570 100644 --- a/router/aspects/request_timeout_handler.go +++ b/router/aspects/request_timeout_handler.go @@ -5,9 +5,9 @@ import ( "strconv" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" ) func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders, metricsEngine metrics.MetricsEngine, requestType metrics.RequestType) httprouter.Handle { diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go index 26e546dcd40..3d6fa34e5bd 100644 --- a/router/aspects/request_timeout_handler_test.go +++ b/router/aspects/request_timeout_handler_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" "github.com/stretchr/testify/assert" ) diff --git a/router/router.go b/router/router.go index 48580f85400..ab76c02ebfb 100644 --- a/router/router.go +++ b/router/router.go @@ -12,41 +12,41 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/endpoints/events" - "github.com/prebid/prebid-server/errortypes" - - "github.com/prebid/prebid-server/metrics" - - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adform" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/conversant" - "github.com/prebid/prebid-server/adapters/ix" - "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/pubmatic" - "github.com/prebid/prebid-server/adapters/pulsepoint" - "github.com/prebid/prebid-server/adapters/rubicon" - "github.com/prebid/prebid-server/adapters/sovrn" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/cache" - "github.com/prebid/prebid-server/cache/dummycache" - "github.com/prebid/prebid-server/cache/filecache" - "github.com/prebid/prebid-server/cache/postgrescache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints" - infoEndpoints "github.com/prebid/prebid-server/endpoints/info" - "github.com/prebid/prebid-server/endpoints/openrtb2" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/router/aspects" - "github.com/prebid/prebid-server/server/ssl" - storedRequestsConf "github.com/prebid/prebid-server/stored_requests/config" - "github.com/prebid/prebid-server/usersync/usersyncers" + "github.com/PubMatic-OpenWrap/prebid-server/currency" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints/events" + "github.com/PubMatic-OpenWrap/prebid-server/errortypes" + + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + + "github.com/PubMatic-OpenWrap/prebid-server/adapters" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" + analyticsConf "github.com/PubMatic-OpenWrap/prebid-server/analytics/config" + "github.com/PubMatic-OpenWrap/prebid-server/cache" + "github.com/PubMatic-OpenWrap/prebid-server/cache/dummycache" + "github.com/PubMatic-OpenWrap/prebid-server/cache/filecache" + "github.com/PubMatic-OpenWrap/prebid-server/cache/postgrescache" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints" + infoEndpoints "github.com/PubMatic-OpenWrap/prebid-server/endpoints/info" + "github.com/PubMatic-OpenWrap/prebid-server/endpoints/openrtb2" + "github.com/PubMatic-OpenWrap/prebid-server/exchange" + "github.com/PubMatic-OpenWrap/prebid-server/gdpr" + metricsConf "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/pbs" + pbc "github.com/PubMatic-OpenWrap/prebid-server/prebid_cache_client" + "github.com/PubMatic-OpenWrap/prebid-server/router/aspects" + "github.com/PubMatic-OpenWrap/prebid-server/server/ssl" + storedRequestsConf "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/config" + "github.com/PubMatic-OpenWrap/prebid-server/usersync/usersyncers" "github.com/golang/glog" "github.com/julienschmidt/httprouter" diff --git a/router/router_test.go b/router/router_test.go index a86d0f72c49..09b07a0a7ce 100644 --- a/router/router_test.go +++ b/router/router_test.go @@ -8,8 +8,8 @@ import ( "os" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/server/listener.go b/server/listener.go index c1f57723da3..f172ad7d961 100644 --- a/server/listener.go +++ b/server/listener.go @@ -5,8 +5,8 @@ import ( "strings" "time" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" "github.com/golang/glog" - "github.com/prebid/prebid-server/metrics" ) // monitorableListener tracks any opened connections in the metrics. diff --git a/server/listener_test.go b/server/listener_test.go index f91dbddbc54..58f3d42d95a 100644 --- a/server/listener_test.go +++ b/server/listener_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" gometrics "github.com/rcrowley/go-metrics" ) diff --git a/server/prometheus.go b/server/prometheus.go index 4b9f7037d0a..8001234faa2 100644 --- a/server/prometheus.go +++ b/server/prometheus.go @@ -7,9 +7,9 @@ import ( "github.com/golang/glog" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prebid/prebid-server/config" - metricsconfig "github.com/prebid/prebid-server/metrics/config" - prometheusMetrics "github.com/prebid/prebid-server/metrics/prometheus" + "github.com/PubMatic-OpenWrap/prebid-server/config" + metricsconfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" + prometheusMetrics "github.com/PubMatic-OpenWrap/prebid-server/metrics/prometheus" ) func newPrometheusServer(cfg *config.Configuration, metrics *metricsconfig.DetailedMetricsEngine) *http.Server { diff --git a/server/server.go b/server/server.go index 46b7e5ae610..0b1aa76b63b 100644 --- a/server/server.go +++ b/server/server.go @@ -12,10 +12,10 @@ import ( "time" "github.com/NYTimes/gziphandler" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + metricsconfig "github.com/PubMatic-OpenWrap/prebid-server/metrics/config" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - metricsconfig "github.com/prebid/prebid-server/metrics/config" ) // Listen blocks forever, serving PBS requests on the given port. This will block forever, until the process is shut down. diff --git a/server/server_test.go b/server/server_test.go index e7ef593a4b5..3d6d5684e96 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/prebid/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/config" ) func TestNewAdminServer(t *testing.T) { diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go index d8cf132d25b..c3b71a3be67 100644 --- a/stored_requests/backends/db_fetcher/fetcher.go +++ b/stored_requests/backends/db_fetcher/fetcher.go @@ -7,8 +7,8 @@ import ( "github.com/lib/pq" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/golang/glog" - "github.com/prebid/prebid-server/stored_requests" ) func NewFetcher(db *sql.DB, queryMaker func(int, int) string) stored_requests.AllFetcher { diff --git a/stored_requests/backends/empty_fetcher/fetcher.go b/stored_requests/backends/empty_fetcher/fetcher.go index ee6b98b3b2e..6edf3cc4d00 100644 --- a/stored_requests/backends/empty_fetcher/fetcher.go +++ b/stored_requests/backends/empty_fetcher/fetcher.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ) // EmptyFetcher is a nil-object which has no Stored Requests. diff --git a/stored_requests/backends/file_fetcher/fetcher.go b/stored_requests/backends/file_fetcher/fetcher.go index 2d3b00657b9..bff94b21e79 100644 --- a/stored_requests/backends/file_fetcher/fetcher.go +++ b/stored_requests/backends/file_fetcher/fetcher.go @@ -7,7 +7,7 @@ import ( "io/ioutil" "strings" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ) // NewFileFetcher _immediately_ loads stored request data from local files. diff --git a/stored_requests/backends/file_fetcher/fetcher_test.go b/stored_requests/backends/file_fetcher/fetcher_test.go index a145a3b43a2..f0900002c8c 100644 --- a/stored_requests/backends/file_fetcher/fetcher_test.go +++ b/stored_requests/backends/file_fetcher/fetcher_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/stretchr/testify/assert" ) diff --git a/stored_requests/backends/http_fetcher/fetcher.go b/stored_requests/backends/http_fetcher/fetcher.go index bc12caecb98..5a7d8fa2878 100644 --- a/stored_requests/backends/http_fetcher/fetcher.go +++ b/stored_requests/backends/http_fetcher/fetcher.go @@ -10,7 +10,7 @@ import ( "net/url" "strings" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/golang/glog" "golang.org/x/net/context/ctxhttp" diff --git a/stored_requests/caches/cachestest/reliable.go b/stored_requests/caches/cachestest/reliable.go index 7fbaf7238af..a0ab07df431 100644 --- a/stored_requests/caches/cachestest/reliable.go +++ b/stored_requests/caches/cachestest/reliable.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ) const ( diff --git a/stored_requests/caches/memory/cache.go b/stored_requests/caches/memory/cache.go index 5939c26ddec..288e6c26b71 100644 --- a/stored_requests/caches/memory/cache.go +++ b/stored_requests/caches/memory/cache.go @@ -5,9 +5,9 @@ import ( "encoding/json" "sync" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" "github.com/coocood/freecache" "github.com/golang/glog" - "github.com/prebid/prebid-server/stored_requests" ) // NewCache returns an in-memory Cache which evicts items if: diff --git a/stored_requests/caches/memory/cache_test.go b/stored_requests/caches/memory/cache_test.go index b89bd5af26f..20ec1239cd2 100644 --- a/stored_requests/caches/memory/cache_test.go +++ b/stored_requests/caches/memory/cache_test.go @@ -7,8 +7,8 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/cachestest" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/cachestest" ) func TestLRURobustness(t *testing.T) { diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go index 7f92f2521cd..ed51ed6f5de 100644 --- a/stored_requests/config/config.go +++ b/stored_requests/config/config.go @@ -6,23 +6,23 @@ import ( "net/http" "time" - "github.com/prebid/prebid-server/metrics" - + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/db_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/file_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/http_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/nil_cache" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + apiEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/api" + httpEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/http" + postgresEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/postgres" + "github.com/PubMatic-OpenWrap/prebid-server/util/task" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/db_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" - "github.com/prebid/prebid-server/stored_requests/caches/memory" - "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" - "github.com/prebid/prebid-server/stored_requests/events" - apiEvents "github.com/prebid/prebid-server/stored_requests/events/api" - httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" - postgresEvents "github.com/prebid/prebid-server/stored_requests/events/postgres" - "github.com/prebid/prebid-server/util/task" ) // This gets set to the connection string used when a database connection is made. We only support a single diff --git a/stored_requests/config/config_test.go b/stored_requests/config/config_test.go index 6c8cd612299..fa1bbfd8764 100644 --- a/stored_requests/config/config_test.go +++ b/stored_requests/config/config_test.go @@ -12,14 +12,14 @@ import ( "github.com/stretchr/testify/assert" sqlmock "github.com/DATA-DOG/go-sqlmock" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/backends/http_fetcher" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + httpEvents "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events/http" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" - "github.com/prebid/prebid-server/stored_requests/events" - httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" "github.com/stretchr/testify/mock" ) diff --git a/stored_requests/events/api/api.go b/stored_requests/events/api/api.go index 6dce4ebaad6..8fb6f6be9eb 100644 --- a/stored_requests/events/api/api.go +++ b/stored_requests/events/api/api.go @@ -5,8 +5,8 @@ import ( "io/ioutil" "net/http" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/stored_requests/events" ) type eventsAPI struct { diff --git a/stored_requests/events/api/api_test.go b/stored_requests/events/api/api_test.go index cd3af77bd83..74e02e69e4d 100644 --- a/stored_requests/events/api/api_test.go +++ b/stored_requests/events/api/api_test.go @@ -9,9 +9,9 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/memory" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" ) func TestGoodRequests(t *testing.T) { diff --git a/stored_requests/events/events.go b/stored_requests/events/events.go index 5b89943572f..60909a0d426 100644 --- a/stored_requests/events/events.go +++ b/stored_requests/events/events.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "github.com/prebid/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" ) // Save represents a bulk save diff --git a/stored_requests/events/events_test.go b/stored_requests/events/events_test.go index f3483705e86..0a48b4cc365 100644 --- a/stored_requests/events/events_test.go +++ b/stored_requests/events/events_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/memory" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/memory" ) func TestListen(t *testing.T) { diff --git a/stored_requests/events/http/http.go b/stored_requests/events/http/http.go index 4615183f693..790c247e368 100644 --- a/stored_requests/events/http/http.go +++ b/stored_requests/events/http/http.go @@ -11,8 +11,8 @@ import ( "golang.org/x/net/context/ctxhttp" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/stored_requests/events" "github.com/golang/glog" ) diff --git a/stored_requests/events/postgres/database.go b/stored_requests/events/postgres/database.go index e769a55585c..89fb30b88c8 100644 --- a/stored_requests/events/postgres/database.go +++ b/stored_requests/events/postgres/database.go @@ -8,11 +8,11 @@ import ( "net" "time" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" + "github.com/PubMatic-OpenWrap/prebid-server/util/timeutil" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/events" - "github.com/prebid/prebid-server/util/timeutil" ) func bytesNull() []byte { diff --git a/stored_requests/events/postgres/database_test.go b/stored_requests/events/postgres/database_test.go index 15d0fbffbc3..63625061dd3 100644 --- a/stored_requests/events/postgres/database_test.go +++ b/stored_requests/events/postgres/database_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go index 865231ee757..597b660cb61 100644 --- a/stored_requests/fetcher.go +++ b/stored_requests/fetcher.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - "github.com/prebid/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" ) // Fetcher knows how to fetch Stored Request data by id. diff --git a/stored_requests/fetcher_test.go b/stored_requests/fetcher_test.go index e77bc75c310..07631e79bbd 100644 --- a/stored_requests/fetcher_test.go +++ b/stored_requests/fetcher_test.go @@ -6,8 +6,8 @@ import ( "errors" "testing" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" + "github.com/PubMatic-OpenWrap/prebid-server/metrics" + "github.com/PubMatic-OpenWrap/prebid-server/stored_requests/caches/nil_cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/usersync/cookie.go b/usersync/cookie.go index c4e329b65e0..ebe15d1c835 100644 --- a/usersync/cookie.go +++ b/usersync/cookie.go @@ -8,8 +8,8 @@ import ( "net/http" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) const ( diff --git a/usersync/cookie_test.go b/usersync/cookie_test.go index ef2e9911e46..c05eadd4a98 100644 --- a/usersync/cookie_test.go +++ b/usersync/cookie_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/usersync/usersync.go b/usersync/usersync.go index 236acbe73a3..7730febcd90 100644 --- a/usersync/usersync.go +++ b/usersync/usersync.go @@ -1,6 +1,6 @@ package usersync -import "github.com/prebid/prebid-server/privacy" +import "github.com/PubMatic-OpenWrap/prebid-server/privacy" type Usersyncer interface { // GetUsersyncInfo returns basic info the browser needs in order to run a user sync. diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index c41f7c6c746..04ec05430e5 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -4,87 +4,87 @@ import ( "strings" "text/template" + ttx "github.com/PubMatic-OpenWrap/prebid-server/adapters/33across" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/acuityads" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adform" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernel" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adkernelAdn" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adman" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/admixer" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adocean" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adpone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtarget" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/adtelligent" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/advangelists" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/aja" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/amx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/appnexus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/audienceNetwork" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/avocet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/beachfront" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/beintoo" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/between" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/brightroll" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/colossus" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/connectad" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/consumable" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/conversant" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/cpmstar" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/datablocks" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/deepintent" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/dmx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/emx_digital" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/engagebdr" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/eplanning" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamma" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gamoshi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/grid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/gumgum" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/improvedigital" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/invibes" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ix" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/krushmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lifestreet" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lockerdome" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/logicad" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/lunamedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/marsmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/mediafuse" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/mgid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/nanointeractive" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ninthdecimal" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/nobid" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/openx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pubmatic" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/pulsepoint" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rhythmone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rtbhouse" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/rubicon" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sharethrough" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartadserver" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartrtb" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/smartyads" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/somoaudience" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sonobi" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/sovrn" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/synacormedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/telaria" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/triplelift_native" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/ucfunnel" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/unruly" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/valueimpression" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/verizonmedia" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/visx" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/vrtcal" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldlab" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldmo" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/yieldone" + "github.com/PubMatic-OpenWrap/prebid-server/adapters/zeroclickfraud" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/usersync" "github.com/golang/glog" - ttx "github.com/prebid/prebid-server/adapters/33across" - "github.com/prebid/prebid-server/adapters/acuityads" - "github.com/prebid/prebid-server/adapters/adform" - "github.com/prebid/prebid-server/adapters/adkernel" - "github.com/prebid/prebid-server/adapters/adkernelAdn" - "github.com/prebid/prebid-server/adapters/adman" - "github.com/prebid/prebid-server/adapters/admixer" - "github.com/prebid/prebid-server/adapters/adocean" - "github.com/prebid/prebid-server/adapters/adpone" - "github.com/prebid/prebid-server/adapters/adtarget" - "github.com/prebid/prebid-server/adapters/adtelligent" - "github.com/prebid/prebid-server/adapters/advangelists" - "github.com/prebid/prebid-server/adapters/aja" - "github.com/prebid/prebid-server/adapters/amx" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/audienceNetwork" - "github.com/prebid/prebid-server/adapters/avocet" - "github.com/prebid/prebid-server/adapters/beachfront" - "github.com/prebid/prebid-server/adapters/beintoo" - "github.com/prebid/prebid-server/adapters/between" - "github.com/prebid/prebid-server/adapters/brightroll" - "github.com/prebid/prebid-server/adapters/colossus" - "github.com/prebid/prebid-server/adapters/connectad" - "github.com/prebid/prebid-server/adapters/consumable" - "github.com/prebid/prebid-server/adapters/conversant" - "github.com/prebid/prebid-server/adapters/cpmstar" - "github.com/prebid/prebid-server/adapters/datablocks" - "github.com/prebid/prebid-server/adapters/deepintent" - "github.com/prebid/prebid-server/adapters/dmx" - "github.com/prebid/prebid-server/adapters/emx_digital" - "github.com/prebid/prebid-server/adapters/engagebdr" - "github.com/prebid/prebid-server/adapters/eplanning" - "github.com/prebid/prebid-server/adapters/gamma" - "github.com/prebid/prebid-server/adapters/gamoshi" - "github.com/prebid/prebid-server/adapters/grid" - "github.com/prebid/prebid-server/adapters/gumgum" - "github.com/prebid/prebid-server/adapters/improvedigital" - "github.com/prebid/prebid-server/adapters/invibes" - "github.com/prebid/prebid-server/adapters/ix" - "github.com/prebid/prebid-server/adapters/krushmedia" - "github.com/prebid/prebid-server/adapters/lifestreet" - "github.com/prebid/prebid-server/adapters/lockerdome" - "github.com/prebid/prebid-server/adapters/logicad" - "github.com/prebid/prebid-server/adapters/lunamedia" - "github.com/prebid/prebid-server/adapters/marsmedia" - "github.com/prebid/prebid-server/adapters/mediafuse" - "github.com/prebid/prebid-server/adapters/mgid" - "github.com/prebid/prebid-server/adapters/nanointeractive" - "github.com/prebid/prebid-server/adapters/ninthdecimal" - "github.com/prebid/prebid-server/adapters/nobid" - "github.com/prebid/prebid-server/adapters/openx" - "github.com/prebid/prebid-server/adapters/pubmatic" - "github.com/prebid/prebid-server/adapters/pulsepoint" - "github.com/prebid/prebid-server/adapters/rhythmone" - "github.com/prebid/prebid-server/adapters/rtbhouse" - "github.com/prebid/prebid-server/adapters/rubicon" - "github.com/prebid/prebid-server/adapters/sharethrough" - "github.com/prebid/prebid-server/adapters/smartadserver" - "github.com/prebid/prebid-server/adapters/smartrtb" - "github.com/prebid/prebid-server/adapters/smartyads" - "github.com/prebid/prebid-server/adapters/somoaudience" - "github.com/prebid/prebid-server/adapters/sonobi" - "github.com/prebid/prebid-server/adapters/sovrn" - "github.com/prebid/prebid-server/adapters/synacormedia" - "github.com/prebid/prebid-server/adapters/telaria" - "github.com/prebid/prebid-server/adapters/triplelift" - "github.com/prebid/prebid-server/adapters/triplelift_native" - "github.com/prebid/prebid-server/adapters/ucfunnel" - "github.com/prebid/prebid-server/adapters/unruly" - "github.com/prebid/prebid-server/adapters/valueimpression" - "github.com/prebid/prebid-server/adapters/verizonmedia" - "github.com/prebid/prebid-server/adapters/visx" - "github.com/prebid/prebid-server/adapters/vrtcal" - "github.com/prebid/prebid-server/adapters/yieldlab" - "github.com/prebid/prebid-server/adapters/yieldmo" - "github.com/prebid/prebid-server/adapters/yieldone" - "github.com/prebid/prebid-server/adapters/zeroclickfraud" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" ) // NewSyncerMap returns a map of all the usersyncer objects. diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 73f0e8861c5..b15301fa8b7 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/PubMatic-OpenWrap/prebid-server/config" + "github.com/PubMatic-OpenWrap/prebid-server/openrtb_ext" ) func TestNewSyncerMap(t *testing.T) { diff --git a/util/httputil/httputil.go b/util/httputil/httputil.go index 461512771b3..93bcca2a8c5 100644 --- a/util/httputil/httputil.go +++ b/util/httputil/httputil.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/prebid/prebid-server/util/iputil" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" ) var ( diff --git a/util/httputil/httputil_test.go b/util/httputil/httputil_test.go index f7166740fe5..7b6a9a504f1 100644 --- a/util/httputil/httputil_test.go +++ b/util/httputil/httputil_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/prebid/prebid-server/util/iputil" + "github.com/PubMatic-OpenWrap/prebid-server/util/iputil" "github.com/stretchr/testify/assert" ) diff --git a/util/task/ticker_task_test.go b/util/task/ticker_task_test.go index 27551c9a2c2..92cf6835ea6 100644 --- a/util/task/ticker_task_test.go +++ b/util/task/ticker_task_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/util/task" + "github.com/PubMatic-OpenWrap/prebid-server/util/task" "github.com/stretchr/testify/assert" ) From 63086feb4ef121e1e6c38067e016bbb9e30edaee Mon Sep 17 00:00:00 2001 From: Isha Bharti Date: Wed, 17 Feb 2021 00:03:33 +0530 Subject: [PATCH 317/318] Adding changes in ctv for prebid upgrade 0.146.0 --- endpoints/openrtb2/ctv_auction.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/endpoints/openrtb2/ctv_auction.go b/endpoints/openrtb2/ctv_auction.go index d207bea0a8d..670bb4c438a 100644 --- a/endpoints/openrtb2/ctv_auction.go +++ b/endpoints/openrtb2/ctv_auction.go @@ -194,7 +194,7 @@ func (deps *ctvEndpointDeps) CTVAuctionEndpoint(w http.ResponseWriter, r *http.R defer cancel() } - response, err = deps.holdAuction(request, usersyncs, account) + response, err = deps.holdAuction(request, usersyncs, account, start) ao.Request = request ao.Response = response @@ -244,7 +244,7 @@ func (deps *ctvEndpointDeps) CTVAuctionEndpoint(w http.ResponseWriter, r *http.R } } -func (deps *ctvEndpointDeps) holdAuction(request *openrtb.BidRequest, usersyncs *usersync.PBSCookie, account *config.Account) (*openrtb.BidResponse, error) { +func (deps *ctvEndpointDeps) holdAuction(request *openrtb.BidRequest, usersyncs *usersync.PBSCookie, account *config.Account, startTime time.Time) (*openrtb.BidResponse, error) { defer util.TimeTrack(time.Now(), fmt.Sprintf("Tid:%v CTVHoldAuction", deps.request.ID)) //Hold OpenRTB Standard Auction @@ -258,6 +258,7 @@ func (deps *ctvEndpointDeps) holdAuction(request *openrtb.BidRequest, usersyncs Account: *account, UserSyncs: usersyncs, RequestType: deps.labels.RType, + StartTime: startTime, LegacyLabels: deps.labels, } From e29129cf0905f43e59444631c6d864e39ff13fef Mon Sep 17 00:00:00 2001 From: Isha Bharti Date: Wed, 17 Feb 2021 20:32:35 +0530 Subject: [PATCH 318/318] UOE-6077: Updating IX usersync --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 8ed388fe8de..116944a3bf3 100755 --- a/config/config.go +++ b/config/config.go @@ -603,7 +603,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGrid, "https://x.bidswitch.net/check_uuid/"+syncRedirectEndpoint+"bidder%3Dgrid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BBSW_UUID%7D?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderGumGum, "https://rtb.gumgum.com/usync/prbds2s?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+syncRedirectEndpoint+"bidder%3Dgumgum%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderImprovedigital, "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r="+syncRedirectEndpoint+"bidder%3Dimprovedigital%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BPUB_USER_ID%7D") - // openrtb_ext.BidderIx doesn't have a good default. + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=186523&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb="+syncRedirectEndpoint+"bidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") // openrtb_ext.BidderInvibes doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderKrushmedia, "https://cs.krushmedia.com/4e4abdd5ecc661643458a730b1aa927d.gif?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+syncRedirectEndpoint+"bidder%3Dkrushmedia%26uid%3D%5BUID%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+syncRedirectEndpoint+"bidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24")