From b2d924f18d09a5848cfafeb7d1ebbf3dcfedc2c2 Mon Sep 17 00:00:00 2001 From: Andre Gielow Date: Thu, 6 Jun 2024 14:51:35 -0400 Subject: [PATCH] TheTradeDesk adapter --- adapters/thetradedesk/params_test.go | 53 +++ adapters/thetradedesk/thetradedesk.go | 190 +++++++++ adapters/thetradedesk/thetradedesk_test.go | 385 ++++++++++++++++++ .../exemplary/simple-banner-cookie-uid.json | 146 +++++++ .../exemplary/simple-banner-inapp.json | 144 +++++++ ...mple-banner-multiple-bids-and-formats.json | 259 ++++++++++++ .../simple-banner-multiple-bids.json | 243 +++++++++++ .../exemplary/simple-banner-uid.json | 170 ++++++++ .../exemplary/simple-multi-type-banner.json | 179 ++++++++ .../exemplary/simple-multi-type-native.json | 104 +++++ .../exemplary/simple-multi-type-video.json | 179 ++++++++ .../exemplary/simple-video.json | 182 +++++++++ .../204-response-from-target.json | 105 +++++ .../400-response-from-target.json | 105 +++++ .../500-response-from-target.json | 105 +++++ .../supplemental/invalid-mtype.json | 86 ++++ .../supplemental/invalid-publisher.json | 45 ++ .../supplemental/simple-banner-with-ipv6.json | 156 +++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_thetradedesk.go | 8 + static/bidder-info/thetradedesk.yaml | 21 + static/bidder-params/thetradedesk.json | 13 + 23 files changed, 2882 insertions(+) create mode 100644 adapters/thetradedesk/params_test.go create mode 100644 adapters/thetradedesk/thetradedesk.go create mode 100644 adapters/thetradedesk/thetradedesk_test.go create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-cookie-uid.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-inapp.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids-and-formats.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-uid.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-banner.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-native.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-video.json create mode 100644 adapters/thetradedesk/thetradedesktest/exemplary/simple-video.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/204-response-from-target.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/400-response-from-target.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/500-response-from-target.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/invalid-mtype.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/invalid-publisher.json create mode 100644 adapters/thetradedesk/thetradedesktest/supplemental/simple-banner-with-ipv6.json create mode 100644 openrtb_ext/imp_thetradedesk.go create mode 100644 static/bidder-info/thetradedesk.yaml create mode 100644 static/bidder-params/thetradedesk.json diff --git a/adapters/thetradedesk/params_test.go b/adapters/thetradedesk/params_test.go new file mode 100644 index 00000000000..f18daed3d56 --- /dev/null +++ b/adapters/thetradedesk/params_test.go @@ -0,0 +1,53 @@ +package thetradedesk + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/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.BidderTheTradeDesk, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected TheTradeDesk params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the TheTradeDesk 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.BidderTheTradeDesk, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"publisherId": "123456"}`, + `{"publisherId": "pub-123456"}`, + `{"publisherId": "publisherIDAllString"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"publisherId": 123456}`, + `{"publisherId": 0}`, +} diff --git a/adapters/thetradedesk/thetradedesk.go b/adapters/thetradedesk/thetradedesk.go new file mode 100644 index 00000000000..f3e31fdbdd4 --- /dev/null +++ b/adapters/thetradedesk/thetradedesk.go @@ -0,0 +1,190 @@ +package thetradedesk + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "regexp" + "text/template" + + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" + + "github.com/prebid/openrtb/v20/openrtb2" +) + +const PREBID_INTEGRATION_TYPE = "1" + +type adapter struct { + bidderEndpoint string +} + +type ExtImpBidderTheTradeDesk struct { + adapters.ExtImpBidder +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + pubID, err := getPublisherId(request.Imp) + + if err != nil { + return nil, []error{err} + } + + modifiedImps := make([]openrtb2.Imp, 0, len(request.Imp)) + + for _, imp := range request.Imp { + + if imp.Banner != nil { + if len(imp.Banner.Format) > 0 { + firstFormat := imp.Banner.Format[0] + bannerCopy := *imp.Banner + bannerCopy.H = &firstFormat.H + bannerCopy.W = &firstFormat.W + imp.Banner = &bannerCopy + + } + } + + modifiedImps = append(modifiedImps, imp) + } + + request.Imp = modifiedImps + + if request.Site != nil { + siteCopy := *request.Site + if siteCopy.Publisher != nil { + publisherCopy := *siteCopy.Publisher + publisherCopy.ID = pubID + siteCopy.Publisher = &publisherCopy + } else { + siteCopy.Publisher = &openrtb2.Publisher{ID: pubID} + } + request.Site = &siteCopy + } else if request.App != nil { + appCopy := *request.App + if appCopy.Publisher != nil { + publisherCopy := *appCopy.Publisher + publisherCopy.ID = pubID + appCopy.Publisher = &publisherCopy + } else { + appCopy.Publisher = &openrtb2.Publisher{ID: pubID} + } + request.App = &appCopy + } + + errs := make([]error, 0, len(request.Imp)) + 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") + headers.Add("x-integration-type", PREBID_INTEGRATION_TYPE) + return []*adapters.RequestData{{ + Method: "POST", + Uri: a.bidderEndpoint, + Body: reqJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }}, errs +} + +func getPublisherId(impressions []openrtb2.Imp) (string, error) { + for _, imp := range impressions { + + var bidderExt ExtImpBidderTheTradeDesk + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return "", err + } + + var ttdExt openrtb_ext.ExtImpTheTradeDesk + if err := json.Unmarshal(bidderExt.Bidder, &ttdExt); err != nil { + return "", err + } + + if ttdExt.PublisherId != "" { + return ttdExt.PublisherId, nil + } + } + return "", nil +} + +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return adapters.NewBidderResponse(), nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{err} + } + + var bidResponse openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResponse); err != nil { + return nil, []error{err} + } + + bidderResponse := adapters.NewBidderResponse() + bidderResponse.Currency = bidResponse.Cur + + for _, seatBid := range bidResponse.SeatBid { + for _, bid := range seatBid.Bid { + bid := bid + + bidType, err := getBidType(bid.MType) + + if err != nil { + return nil, []error{err} + } + + b := &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + } + bidderResponse.Bids = append(bidderResponse.Bids, b) + } + } + + return bidderResponse, nil +} + +func getBidType(markupType openrtb2.MarkupType) (openrtb_ext.BidType, error) { + switch markupType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + return "", fmt.Errorf("unsupported mtype: %d", markupType) + } +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + template, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + isValidEndpoint, err := regexp.Match("([a-z]*)$", []byte(config.ExtraAdapterInfo)) + if !isValidEndpoint || err != nil { + return nil, errors.New("ExtraAdapterInfo must be a simple string provided by TheTradeDesk") + } + + urlParams := macros.EndpointTemplateParams{SupplyId: config.ExtraAdapterInfo} + bidderEndpoint, err := macros.ResolveMacros(template, urlParams) + + if err != nil { + return nil, fmt.Errorf("unable to resolve endpoint macros: %v", err) + } + + return &adapter{ + bidderEndpoint: bidderEndpoint, + }, nil +} diff --git a/adapters/thetradedesk/thetradedesk_test.go b/adapters/thetradedesk/thetradedesk_test.go new file mode 100644 index 00000000000..fcee1e26a3a --- /dev/null +++ b/adapters/thetradedesk/thetradedesk_test.go @@ -0,0 +1,385 @@ +package thetradedesk + +import ( + "encoding/json" + "net/http" + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderTheTradeDesk, config.Adapter{ + Endpoint: "{{Malformed}}"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} + +func TestBadConfig(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderTheTradeDesk, config.Adapter{ + Endpoint: `http://it.doesnt.matter/bid`, + ExtraAdapterInfo: `{foo:42}`, + }, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} + +func TestCorrectConfig(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTheTradeDesk, config.Adapter{ + Endpoint: `http://it.doesnt.matter/bid`, + ExtraAdapterInfo: `abcde`, + }, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.NoError(t, buildErr) + assert.NotNil(t, bidder) +} + +func TestEmptyConfig(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTheTradeDesk, config.Adapter{ + Endpoint: `https://direct.adsrvr.org/bid/bidder/{{.SupplyId}}`, + ExtraAdapterInfo: `ttd`, + }, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.NoError(t, buildErr) + assert.NotNil(t, bidder) +} + +func TestJsonSamples(t *testing.T) { + bidder, err := Builder( + openrtb_ext.BidderTheTradeDesk, + config.Adapter{Endpoint: "https://direct.adsrvr.org/bid/bidder/{{.SupplyId}}", ExtraAdapterInfo: "ttd"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "1"}, + ) + assert.Nil(t, err) + adapterstest.RunJSONBidderTest(t, "thetradedesktest", bidder) +} + +func TestGetBidType(t *testing.T) { + type args struct { + markupType openrtb2.MarkupType + } + tests := []struct { + name string + args args + markupType openrtb2.MarkupType + expectedBidTypeId openrtb_ext.BidType + wantErr bool + }{ + { + name: "getBidType banner", + args: args{ + markupType: openrtb2.MarkupBanner, + }, + expectedBidTypeId: openrtb_ext.BidTypeBanner, + wantErr: false, + }, + { + name: "getBidType video", + args: args{ + markupType: openrtb2.MarkupVideo, + }, + expectedBidTypeId: openrtb_ext.BidTypeVideo, + wantErr: false, + }, + { + name: "getBidType native", + args: args{ + markupType: openrtb2.MarkupNative, + }, + expectedBidTypeId: openrtb_ext.BidTypeNative, + wantErr: false, + }, + { + name: "getBidType invalid", + args: args{ + markupType: -1, + }, + expectedBidTypeId: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bidType, err := getBidType(tt.args.markupType) + assert.Equal(t, tt.wantErr, err != nil) + assert.Equal(t, tt.expectedBidTypeId, bidType) + }) + } +} + +func TestGetPublisherId(t *testing.T) { + type args struct { + impressions []openrtb2.Imp + } + tests := []struct { + name string + args args + expectedPublisherId string + wantErr bool + }{ + { + name: "valid publisher Id", + args: args{ + impressions: []openrtb2.Imp{ + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{"publisherId":"1"}}`), + }, + }, + }, + expectedPublisherId: "1", + wantErr: false, + }, + { + name: "multiple valid publisher Id", + args: args{ + impressions: []openrtb2.Imp{ + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{"publisherId":"1"}}`), + }, + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{"publisherId":"2"}}`), + }, + }, + }, + expectedPublisherId: "1", + wantErr: false, + }, + { + name: "not publisherId present", + args: args{ + impressions: []openrtb2.Imp{ + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{}}`), + }, + }, + }, + expectedPublisherId: "", + wantErr: false, + }, + { + name: "nil publisherId present", + args: args{ + impressions: []openrtb2.Imp{ + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{"publisherId":""}}`), + }, + }, + }, + expectedPublisherId: "", + wantErr: false, + }, + { + name: "no impressions", + args: args{ + impressions: []openrtb2.Imp{}, + }, + expectedPublisherId: "", + wantErr: false, + }, + { + name: "invalid bidder object", + args: args{ + impressions: []openrtb2.Imp{ + { + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"bidder":{"doesnotexistprop":""}}`), + }, + }, + }, + expectedPublisherId: "", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + publisherId, err := getPublisherId(tt.args.impressions) + assert.Equal(t, tt.wantErr, err != nil) + assert.Equal(t, tt.expectedPublisherId, publisherId) + }) + } +} + +func TestTheTradeDeskAdapter_MakeRequests(t *testing.T) { + type fields struct { + URI string + } + type args struct { + request *openrtb2.BidRequest + reqInfo *adapters.ExtraRequestInfo + } + tests := []struct { + name string + fields fields + args args + expectedReqData []*adapters.RequestData + wantErr bool + }{ + { + name: "invalid bidderparams", + args: args{ + request: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams":{:"123"}}}`)}, + }, + wantErr: true, + }, + { + name: "request with App", + args: args{ + request: &openrtb2.BidRequest{ + App: &openrtb2.App{}, + Ext: json.RawMessage(`{"prebid":{"bidderparams":{"wrapper":"123"}}}`), + }, + }, + wantErr: false, + }, + { + name: "request with App and publisher", + args: args{ + request: &openrtb2.BidRequest{ + App: &openrtb2.App{Publisher: &openrtb2.Publisher{}}, + Ext: json.RawMessage(`{"prebid":{"bidderparams":{"wrapper":"123"}}}`), + }, + }, + wantErr: false, + }, + { + name: "request with Site", + args: args{ + request: &openrtb2.BidRequest{ + Site: &openrtb2.Site{}, + Ext: json.RawMessage(`{"prebid":{"bidderparams":{"wrapper":"123"}}}`), + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &adapter{ + bidderEndpoint: tt.fields.URI, + } + gotReqData, gotErr := a.MakeRequests(tt.args.request, tt.args.reqInfo) + assert.Equal(t, tt.wantErr, len(gotErr) != 0) + if tt.wantErr == false { + assert.NotNil(t, gotReqData) + } + }) + } +} + +func TestTheTradeDeskAdapter_MakeBids(t *testing.T) { + type fields struct { + URI string + } + type args struct { + internalRequest *openrtb2.BidRequest + externalRequest *adapters.RequestData + response *adapters.ResponseData + } + tests := []struct { + name string + fields fields + args args + wantErr []error + wantResp *adapters.BidderResponse + }{ + { + name: "happy path, valid response with all bid params", + args: args{ + response: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"mtype": 1, "id": "7706636740145184841", "impid": "test-imp-id", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["ttd.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":{"dspid": 6, "deal_channel": 1, "prebiddealpriority": 1}}]}], "bidid": "5778926625248726496", "cur": "USD"}`), + }, + }, + wantErr: nil, + wantResp: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "7706636740145184841", + ImpID: "test-imp-id", + Price: 0.500000, + AdID: "29681110", + AdM: "some-test-ad", + ADomain: []string{"ttd.com"}, + CrID: "29681110", + H: 250, + W: 300, + DealID: "testdeal", + Ext: json.RawMessage(`{"dspid": 6, "deal_channel": 1, "prebiddealpriority": 1}`), + MType: openrtb2.MarkupBanner, + }, + BidType: openrtb_ext.BidTypeBanner, + }, + }, + Currency: "USD", + }, + }, + { + name: "ignore invalid prebiddealpriority", + args: args{ + response: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{"id": "test-request-id", "seatbid":[{"seat": "958", "bid":[{"mtype": 2, "id": "7706636740145184841", "impid": "test-imp-id", "price": 0.500000, "adid": "29681110", "adm": "some-test-ad", "adomain":["ttd.com"], "crid": "29681110", "h": 250, "w": 300, "dealid": "testdeal", "ext":{"dspid": 6, "deal_channel": 1, "prebiddealpriority": -1}}]}], "bidid": "5778926625248726496", "cur": "USD"}`), + }, + }, + wantErr: nil, + wantResp: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "7706636740145184841", + ImpID: "test-imp-id", + Price: 0.500000, + AdID: "29681110", + AdM: "some-test-ad", + ADomain: []string{"ttd.com"}, + CrID: "29681110", + H: 250, + W: 300, + DealID: "testdeal", + Ext: json.RawMessage(`{"dspid": 6, "deal_channel": 1, "prebiddealpriority": -1}`), + MType: openrtb2.MarkupVideo, + }, + BidType: openrtb_ext.BidTypeVideo, + }, + }, + Currency: "USD", + }, + }, + { + name: "no content response", + args: args{ + response: &adapters.ResponseData{ + StatusCode: http.StatusNoContent, + Body: nil, + }, + }, + wantErr: nil, + wantResp: adapters.NewBidderResponse(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &adapter{ + bidderEndpoint: tt.fields.URI, + } + gotResp, gotErr := a.MakeBids(tt.args.internalRequest, tt.args.externalRequest, tt.args.response) + assert.Equal(t, tt.wantErr, gotErr, gotErr) + assert.Equal(t, tt.wantResp, gotResp) + }) + } +} diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-cookie-uid.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-cookie-uid.json new file mode 100644 index 00000000000..d75c8e11c44 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-cookie-uid.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "buyeruid": "ttd-user-id" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "buyeruid": "ttd-user-id" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-inapp.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-inapp.json new file mode 100644 index 00000000000..f776aa64b45 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-inapp.json @@ -0,0 +1,144 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "this_id_will_be_replaced" + } + }, + "device": { + "ifa": "test-ifa-123456", + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "123456" + } + }, + "device": { + "ifa": "test-ifa-123456", + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner", + "networkName": "TheTradeDesk" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner", + "networkName": "TheTradeDesk" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids-and-formats.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids-and-formats.json new file mode 100644 index 00000000000..fc90ae93391 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids-and-formats.json @@ -0,0 +1,259 @@ +{ + "mockBidRequest": { + "id": "test-request-id-multiple-bids", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 500, + "h": 300 + } + ], + "w": 100, + "h": 150 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 500, + "h": 300 + } + ], + "w": 100, + "h": 150 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id-multiple-bids", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 500, + "h": 300 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 500, + "h": 300 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id","test-imp-id2"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + }, + { + "bid": [ + { + "id": "test-slot-id2", + "impid": "test-imp-id2", + "price": 0.5, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "test-slot-id2", + "impid": "test-imp-id2", + "price": 0.5, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids.json new file mode 100644 index 00000000000..73bbc347fde --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-multiple-bids.json @@ -0,0 +1,243 @@ +{ + "mockBidRequest": { + "id": "test-request-id-multiple-bids", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id-multiple-bids", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id","test-imp-id2"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + }, + { + "bid": [ + { + "id": "test-slot-id2", + "impid": "test-imp-id2", + "price": 0.5, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "test-slot-id2", + "impid": "test-imp-id2", + "price": 0.5, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-uid.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-uid.json new file mode 100644 index 00000000000..0ce80789889 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-banner-uid.json @@ -0,0 +1,170 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-banner.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-banner.json new file mode 100644 index 00000000000..f5871f6c058 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-banner.json @@ -0,0 +1,179 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-native.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-native.json new file mode 100644 index 00000000000..6fa3264566a --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-native.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "native" + } + }, + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "native" + } + }, + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-video.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-video.json new file mode 100644 index 00000000000..570bb78f119 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-multi-type-video.json @@ -0,0 +1,179 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 300, + "h": 250 + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "some-test-ad-vast", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "video" + } + }, + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "some-test-ad-vast", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "video" + } + }, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/exemplary/simple-video.json b/adapters/thetradedesk/thetradedesktest/exemplary/simple-video.json new file mode 100644 index 00000000000..ff82218191d --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/exemplary/simple-video.json @@ -0,0 +1,182 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "pbs-local/preroll", + "video": { + "minduration": 0, + "maxduration": 60, + "api": [1,2], + "mimes": [ + "video/mp4", + "video/webm", + "application/javascript" + ], + "placement": 1, + "protocols": [2,3,4,5,6], + "w": 300, + "h": 250, + "playbackmethod": [1,2,3,4,5,6], + "plcmt": 1, + "skip": 1 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "pbs-local/preroll", + "video": { + "maxduration": 60, + "api": [1,2], + "mimes": [ + "video/mp4", + "video/webm", + "application/javascript" + ], + "placement": 1, + "protocols": [2,3,4,5,6], + "w": 300, + "h": 250, + "playbackmethod": [1,2,3,4,5,6], + "plcmt": 1, + "skip": 1 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "some-test-ad-vast", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "video" + } + }, + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "some-test-ad-vast", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "video" + } + }, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/204-response-from-target.json b/adapters/thetradedesk/thetradedesktest/supplemental/204-response-from-target.json new file mode 100644 index 00000000000..a329982ea50 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/204-response-from-target.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle" + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "123456" + } + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [] + } + ] + } diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/400-response-from-target.json b/adapters/thetradedesk/thetradedesktest/supplemental/400-response-from-target.json new file mode 100644 index 00000000000..ad5ffc62b51 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/400-response-from-target.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle" + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "123456" + } + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + } diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/500-response-from-target.json b/adapters/thetradedesk/thetradedesktest/supplemental/500-response-from-target.json new file mode 100644 index 00000000000..f2ccb342113 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/500-response-from-target.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle" + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "123456" + } + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 500 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + } diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/invalid-mtype.json b/adapters/thetradedesk/thetradedesktest/supplemental/invalid-mtype.json new file mode 100644 index 00000000000..52b392eb835 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/invalid-mtype.json @@ -0,0 +1,86 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "h": 250, + "w": 300, + "ext": { + "prebid": { + "type": "native" + } + }, + "mtype": -1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "unsupported mtype: -1", + "comparison": "literal" + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/invalid-publisher.json b/adapters/thetradedesk/thetradedesktest/supplemental/invalid-publisher.json new file mode 100644 index 00000000000..9e936f38f20 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/invalid-publisher.json @@ -0,0 +1,45 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "test.app.bundle", + "publisher": { + "id": "this_id_will_be_replaced" + } + }, + "device": { + "ifa": "test-ifa-123456", + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": 123456 + } + } + } + ] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal number into Go struct field ExtImpTheTradeDesk.publisherId of type string", + "comparison": "literal" + } + ] +} + diff --git a/adapters/thetradedesk/thetradedesktest/supplemental/simple-banner-with-ipv6.json b/adapters/thetradedesk/thetradedesktest/supplemental/simple-banner-with-ipv6.json new file mode 100644 index 00000000000..b39f210ba99 --- /dev/null +++ b/adapters/thetradedesk/thetradedesktest/supplemental/simple-banner-with-ipv6.json @@ -0,0 +1,156 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com" + }, + "device": { + "os": "android", + "ipv6": "fd36:ce97:0fa1:dec0:0000:0000:0000:0000", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://direct.adsrvr.org/bid/bidder/ttd", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Integration-Type": ["1"] + }, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "ttd.com", + "publisher": { + "id": "123456" + } + }, + "device": { + "os": "android", + "ipv6": "fd36:ce97:0fa1:dec0:0000:0000:0000:0000", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "ttd.com", + "uids": [ + { + "id": "ttd-uid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "publisherId": "123456" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + }, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 09c7bf83777..45d91aaa6eb 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -178,6 +178,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/teads" "github.com/prebid/prebid-server/v2/adapters/telaria" "github.com/prebid/prebid-server/v2/adapters/theadx" + "github.com/prebid/prebid-server/v2/adapters/thetradedesk" "github.com/prebid/prebid-server/v2/adapters/tpmn" "github.com/prebid/prebid-server/v2/adapters/trafficgate" "github.com/prebid/prebid-server/v2/adapters/triplelift" @@ -390,6 +391,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderTeads: teads.Builder, openrtb_ext.BidderTelaria: telaria.Builder, openrtb_ext.BidderTheadx: theadx.Builder, + openrtb_ext.BidderTheTradeDesk: thetradedesk.Builder, openrtb_ext.BidderTpmn: tpmn.Builder, openrtb_ext.BidderTrafficGate: trafficgate.Builder, openrtb_ext.BidderTriplelift: triplelift.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index b6cda02b89c..e11cc7754a1 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -196,6 +196,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderTeads, BidderTelaria, BidderTheadx, + BidderTheTradeDesk, BidderTpmn, BidderTrafficGate, BidderTriplelift, @@ -506,6 +507,7 @@ const ( BidderTeads BidderName = "teads" BidderTelaria BidderName = "telaria" BidderTheadx BidderName = "theadx" + BidderTheTradeDesk BidderName = "thetradedesk" BidderTpmn BidderName = "tpmn" BidderTrafficGate BidderName = "trafficgate" BidderTriplelift BidderName = "triplelift" diff --git a/openrtb_ext/imp_thetradedesk.go b/openrtb_ext/imp_thetradedesk.go new file mode 100644 index 00000000000..89cca83f65e --- /dev/null +++ b/openrtb_ext/imp_thetradedesk.go @@ -0,0 +1,8 @@ +package openrtb_ext + +// ExtImpTheTradeDesk defines the contract for bidrequest.imp[i].ext +// PublisherId is mandatory parameters, others are optional parameters + +type ExtImpTheTradeDesk struct { + PublisherId string `json:"publisherId"` +} diff --git a/static/bidder-info/thetradedesk.yaml b/static/bidder-info/thetradedesk.yaml new file mode 100644 index 00000000000..cdacb4e240c --- /dev/null +++ b/static/bidder-info/thetradedesk.yaml @@ -0,0 +1,21 @@ +endpoint: "https://direct.adsrvr.org/bid/bidder/{{.SupplyId}}" +maintainer: + email: "Prebid-Maintainers@thetradedesk.com" +gvlVendorID: 21 +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native +userSync: + redirect: + url: https://match.adsrvr.org?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID +openrtb: + gpp-supported: true diff --git a/static/bidder-params/thetradedesk.json b/static/bidder-params/thetradedesk.json new file mode 100644 index 00000000000..5a85cf2f516 --- /dev/null +++ b/static/bidder-params/thetradedesk.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "The Trade Desk Adapter Params", + "description": "A schema which validates params accepted by the The Trade Desk adapter", + "type": "object", + "properties": { + "publisherId": { + "type": "string", + "description": "An ID which identifies the publisher" + } + }, + "required": ["publisherId"] +}