From c05c013185580dd8ea5d7bdf5a93286aedfb18d9 Mon Sep 17 00:00:00 2001 From: moroz Date: Thu, 28 May 2020 16:20:02 +0300 Subject: [PATCH 1/2] Add Adtarget server adapter --- adapters/adtarget/adtarget.go | 190 ++++++++++++++++++ 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 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_adtarget.go | 9 + static/bidder-info/adtarget.yaml | 10 + static/bidder-params/adtarget.json | 26 +++ usersync/usersyncers/syncer.go | 2 + usersync/usersyncers/syncer_test.go | 1 + 23 files changed, 786 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 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..ed92936ea1c --- /dev/null +++ b/adapters/adtarget/adtarget.go @@ -0,0 +1,190 @@ +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 := validateImpression(&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, impIds := range imp2source { + request.Imp = request.Imp[:0] + + for i := 0; i < len(impIds); i++ { + request.Imp = append(request.Imp, imps[impIds[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, + }) + } + + if 0 == len(reqs) { + return nil, errors + } + + 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 + } + + 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 validateImpression(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 4c1d6983bb8..3b77d996e4d 100755 --- a/config/config.go +++ b/config/config.go @@ -498,6 +498,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&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. @@ -698,6 +699,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://hb.adtelligent.com/auction") 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/exchange/adapter_map.go b/exchange/adapter_map.go index 9110b20ffff..47ebb60534e 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -15,6 +15,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" @@ -92,6 +93,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 a0a8c6a8929..f84d44f3bfe 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -32,6 +32,7 @@ const ( BidderAdpone BidderName = "adpone" BidderAdmixer BidderName = "admixer" BidderAdOcean BidderName = "adocean" + BidderAdtarget BidderName = "adtarget" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" BidderAJA BidderName = "aja" @@ -105,6 +106,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..48b48d184aa --- /dev/null +++ b/static/bidder-info/adtarget.yaml @@ -0,0 +1,10 @@ +maintainer: + email: "kamil@adtarget.com.tr" +capabilities: + app: + mediaTypes: + - banner + 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 3f12ee7f728..6870b237542 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" @@ -82,6 +83,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 49ce85d0910..c0b0ad3becb 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 1592b3ea16160425c2c61e98cb2cc7d99ba8610b Mon Sep 17 00:00:00 2001 From: moroz Date: Mon, 8 Jun 2020 19:28:49 +0300 Subject: [PATCH 2/2] Suggested changes for Adtarget --- adapters/adtarget/adtarget.go | 21 ++++++++++----------- docs/bidders/adtarget.md | 5 +++++ static/bidder-info/adtarget.yaml | 1 + 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 docs/bidders/adtarget.md diff --git a/adapters/adtarget/adtarget.go b/adapters/adtarget/adtarget.go index ed92936ea1c..77622d458a4 100644 --- a/adapters/adtarget/adtarget.go +++ b/adapters/adtarget/adtarget.go @@ -27,7 +27,7 @@ func (a *AdtargetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada for i := 0; i < totalImps; i++ { - sourceId, err := validateImpression(&request.Imp[i]) + sourceId, err := validateImpressionAndSetExt(&request.Imp[i]) if err != nil { errors = append(errors, err) @@ -55,11 +55,11 @@ func (a *AdtargetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada imps := request.Imp request.Imp = make([]openrtb.Imp, 0, len(imps)) - for sourceId, impIds := range imp2source { + for sourceId, impIndexes := range imp2source { request.Imp = request.Imp[:0] - for i := 0; i < len(impIds); i++ { - request.Imp = append(request.Imp, imps[impIds[i]]) + for i := 0; i < len(impIndexes); i++ { + request.Imp = append(request.Imp, imps[impIndexes[i]]) } body, err := json.Marshal(request) @@ -76,12 +76,7 @@ func (a *AdtargetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada }) } - if 0 == len(reqs) { - return nil, errors - } - return reqs, errors - } func (a *AdtargetAdapter) MakeBids(bidReq *openrtb.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { @@ -89,7 +84,11 @@ func (a *AdtargetAdapter) MakeBids(bidReq *openrtb.BidRequest, unused *adapters. 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{ @@ -137,7 +136,7 @@ func (a *AdtargetAdapter) MakeBids(bidReq *openrtb.BidRequest, unused *adapters. return bidResponse, errors } -func validateImpression(imp *openrtb.Imp) (int, error) { +func validateImpressionAndSetExt(imp *openrtb.Imp) (int, error) { if imp.Banner == nil && imp.Video == nil { return 0, &errortypes.BadInput{ 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/static/bidder-info/adtarget.yaml b/static/bidder-info/adtarget.yaml index 48b48d184aa..d52f18ac697 100644 --- a/static/bidder-info/adtarget.yaml +++ b/static/bidder-info/adtarget.yaml @@ -4,6 +4,7 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner