diff --git a/adapters/trustx/params_test.go b/adapters/trustx/params_test.go new file mode 100644 index 00000000000..e0af176ca2d --- /dev/null +++ b/adapters/trustx/params_test.go @@ -0,0 +1,51 @@ +package trustx + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/require" +) + +// This file actually intends to test static/bidder-params/trustx.json +// +// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.trustx + +// TestValidParams makes sure that the trustx schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + require.NoError(t, err, "Failed to fetch the json-schemas") + + for _, validParam := range validParams { + err := validator.Validate(openrtb_ext.BidderTrustX, json.RawMessage(validParam)) + require.NoError(t, err, "Schema rejected trustx params: %s", validParam) + } +} + +// TestInvalidParams makes sure that the trustx schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + require.NoError(t, err, "Failed to fetch the json-schemas") + + for _, invalidParam := range invalidParams { + err := validator.Validate(openrtb_ext.BidderTrustX, json.RawMessage(invalidParam)) + require.Error(t, err, "Schema allowed unexpected params: %s", invalidParam) + } +} + +var validParams = []string{ + `{}`, + `{"uid": 1234}`, + `{"uid": 1234, "keywords":{"site": {}, "user": {}}}`, +} +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{"uid": "invalid_type"}`, + `{"keywords": "invalid_type"}`, +} diff --git a/adapters/trustx/trustx.go b/adapters/trustx/trustx.go new file mode 100644 index 00000000000..fca97ac88e6 --- /dev/null +++ b/adapters/trustx/trustx.go @@ -0,0 +1,209 @@ +package trustx + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +type impExt struct { + Prebid *openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` + Bidder json.RawMessage `json:"bidder"` + Data *impExtData `json:"data,omitempty"` + Gpid string `json:"gpid,omitempty"` +} + +type impExtData struct { + PbAdslot string `json:"pbadslot,omitempty"` + AdServer *impExtDataAdServer `json:"adserver,omitempty"` +} + +type impExtDataAdServer struct { + Name string `json:"name,omitempty"` + AdSlot string `json:"adslot,omitempty"` +} + +type bidExt struct { + Bidder bidExtBidder `json:"bidder,omitempty"` +} + +type bidExtBidder struct { + TrustX bidExtBidderTrustX `json:"trustx,omitempty"` +} + +type bidExtBidderTrustX struct { + NetworkName string `json:"networkName,omitempty"` +} + +type adapter struct { + endpoint string +} + +// Builder builds a new instance of the TRUSTX adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + bidder := &adapter{ + endpoint: config.Endpoint, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + for i := range request.Imp { + setImpExtData(&request.Imp[i]) + } + + body, err := jsonutil.Marshal(request) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + reqs := make([]*adapters.RequestData, 0, 1) + requestData := &adapters.RequestData{ + Method: http.MethodPost, + Uri: a.endpoint, + Body: body, + Headers: getHeaders(request), + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + } + reqs = append(reqs, requestData) + + return reqs, errs +} + +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{err} + } + + var resp openrtb2.BidResponse + if err := jsonutil.Unmarshal(response.Body, &resp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Bad Server Response", + }} + } + + var bidErrors []error + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(1) + for i := range resp.SeatBid { + seatBid := &resp.SeatBid[i] + for j := range seatBid.Bid { + bid := &seatBid.Bid[j] + typedBid, err := getTypedBid(bid) + if err != nil { + bidErrors = append(bidErrors, err) + continue + } + bidderResponse.Bids = append(bidderResponse.Bids, typedBid) + } + } + + return bidderResponse, bidErrors +} + +func setImpExtData(imp *openrtb2.Imp) { + var ext impExt + if err := jsonutil.Unmarshal(imp.Ext, &ext); err != nil { + return + } + if ext.Data != nil && ext.Data.AdServer != nil && ext.Data.AdServer.AdSlot != "" { + ext.Gpid = ext.Data.AdServer.AdSlot + extJSON, err := jsonutil.Marshal(ext) + if err == nil { + imp.Ext = extJSON + } + } +} + +func getHeaders(request *openrtb2.BidRequest) http.Header { + headers := http.Header{} + headers.Set("Content-Type", "application/json;charset=utf-8") + headers.Set("Accept", "application/json") + headers.Set("X-Openrtb-Version", "2.6") + + if request.Site != nil { + if request.Site.Ref != "" { + headers.Set("Referer", request.Site.Ref) + } + if request.Site.Domain != "" { + headers.Set("Origin", request.Site.Domain) + } + } + + if request.Device != nil { + if len(request.Device.IP) > 0 { + headers.Set("X-Forwarded-For", request.Device.IP) + } + + if len(request.Device.IPv6) > 0 { + headers.Set("X-Forwarded-For", request.Device.IPv6) + } + + if len(request.Device.UA) > 0 { + headers.Set("User-Agent", request.Device.UA) + } + } + + return headers +} + +func getTypedBid(bid *openrtb2.Bid) (*adapters.TypedBid, error) { + var bidType openrtb_ext.BidType + switch bid.MType { + case openrtb2.MarkupBanner: + bidType = openrtb_ext.BidTypeBanner + case openrtb2.MarkupVideo: + bidType = openrtb_ext.BidTypeVideo + default: + return nil, &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unsupported MType: %v", bid.MType), + } + } + + var extBidPrebidVideo *openrtb_ext.ExtBidPrebidVideo + if bidType == openrtb_ext.BidTypeVideo { + extBidPrebidVideo = &openrtb_ext.ExtBidPrebidVideo{} + if len(bid.Cat) > 0 { + extBidPrebidVideo.PrimaryCategory = bid.Cat[0] + } + if bid.Dur > 0 { + extBidPrebidVideo.Duration = int(bid.Dur) + } + } + return &adapters.TypedBid{ + Bid: bid, + BidType: bidType, + BidVideo: extBidPrebidVideo, + BidMeta: getBidMeta(bid.Ext), + }, nil +} + +func getBidMeta(ext json.RawMessage) *openrtb_ext.ExtBidPrebidMeta { + if ext == nil { + return nil + } + var be bidExt + + if err := jsonutil.Unmarshal(ext, &be); err != nil { + return nil + } + var bidMeta *openrtb_ext.ExtBidPrebidMeta + if be.Bidder.TrustX.NetworkName != "" { + bidMeta = &openrtb_ext.ExtBidPrebidMeta{ + NetworkName: be.Bidder.TrustX.NetworkName, + } + } + return bidMeta +} diff --git a/adapters/trustx/trustx_test.go b/adapters/trustx/trustx_test.go new file mode 100644 index 00000000000..dbfc0078263 --- /dev/null +++ b/adapters/trustx/trustx_test.go @@ -0,0 +1,18 @@ +package trustx + +import ( + "testing" + + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/require" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTrustX, config.Adapter{ + Endpoint: "https://test.localhost.com"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + require.NoError(t, buildErr, "Builder returned unexpected error") + adapterstest.RunJSONBidderTest(t, "trustxtest", bidder) +} diff --git a/adapters/trustx/trustxtest/exemplary/multiple-imp-types.json b/adapters/trustx/trustxtest/exemplary/multiple-imp-types.json new file mode 100644 index 00000000000..6e0f3511ee1 --- /dev/null +++ b/adapters/trustx/trustxtest/exemplary/multiple-imp-types.json @@ -0,0 +1,190 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + }, + { + "id": "test-imp-2", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "test-domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + }, + { + "id": "test-imp-2", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "device": { + "ip": "123.123.123.123", + "ua": "ua" + }, + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index" + } + }, + "impIDs": [ + "test-imp", + "test-imp-2" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + }, + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d708", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d223", + "price": 10, + "impid": "test-imp-2", + "adm": "", + "mtype": 1 + } + ], + "seat": "trustx" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB3-8" + } + }, + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d708", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d223", + "price": 10, + "impid": "test-imp-2", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/trustx/trustxtest/exemplary/simple-app-banner.json b/adapters/trustx/trustxtest/exemplary/simple-app-banner.json new file mode 100644 index 00000000000..14351d090d8 --- /dev/null +++ b/adapters/trustx/trustxtest/exemplary/simple-app-banner.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "data": { + "adserver": { + "adslot": "test_ad_slot" + } + }, + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ua": "ua", + "ip": "123.123.123.123" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "https://test.localhost.com", + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "gpid": "test_ad_slot", + "data": { + "adserver": { + "adslot": "test_ad_slot" + } + }, + "bidder": { + "uid": 1234 + } + } + } + ], + "device": { + "ua": "ua", + "ip": "123.123.123.123" + }, + "app": { + "bundle": "app-bundle" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1, + "ext": { + "bidder": { + "trustx": { + "networkName": "test-network" + } + } + } + } + ] + } + ], + "seat": "trustx" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1, + "ext": { + "bidder": { + "trustx": { + "networkName": "test-network" + } + } + } + }, + "meta": { + "networkName": "test-network" + }, + "type": "banner" + } + ], + "seat": "trustx" + } + ] +} diff --git a/adapters/trustx/trustxtest/exemplary/simple-app-video.json b/adapters/trustx/trustxtest/exemplary/simple-app-video.json new file mode 100644 index 00000000000..5bd5deee4c7 --- /dev/null +++ b/adapters/trustx/trustxtest/exemplary/simple-app-video.json @@ -0,0 +1,141 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + } + ], + "seat": "trustx" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB3-8" + } + } + ] + } + ] +} + diff --git a/adapters/trustx/trustxtest/exemplary/simple-web-banner.json b/adapters/trustx/trustxtest/exemplary/simple-web-banner.json new file mode 100644 index 00000000000..5771a229b9d --- /dev/null +++ b/adapters/trustx/trustxtest/exemplary/simple-web-banner.json @@ -0,0 +1,128 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index", + "ref": "https://example.com/page" + }, + "device": { + "ua": "ua", + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "test-domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + ], + "Referer": [ + "https://example.com/page" + ] + }, + "uri": "https://test.localhost.com", + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index", + "ref": "https://example.com/page" + }, + "device": { + "ua": "ua", + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1 + } + ] + } + ], + "seat": "trustx" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ], + "seat": "trustx" + } + ] +} diff --git a/adapters/trustx/trustxtest/exemplary/simple-web-video.json b/adapters/trustx/trustxtest/exemplary/simple-web-video.json new file mode 100644 index 00000000000..aee0c04e990 --- /dev/null +++ b/adapters/trustx/trustxtest/exemplary/simple-web-video.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "test-domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + } + ], + "seat": "trustx" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB3-8" + } + } + ] + } + ] +} + diff --git a/adapters/trustx/trustxtest/supplemental/bad-server-response.json b/adapters/trustx/trustxtest/supplemental/bad-server-response.json new file mode 100644 index 00000000000..7f5221585c8 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/bad-server-response.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Bad Server Response", + "comparison": "literal" + } + ] +} + diff --git a/adapters/trustx/trustxtest/supplemental/bad-status-400.json b/adapters/trustx/trustxtest/supplemental/bad-status-400.json new file mode 100644 index 00000000000..0786d488913 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/bad-status-400.json @@ -0,0 +1,98 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.org/index" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "test-domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "https://test.localhost.com", + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.org/index" + }, + "device": { + "ua": "ua", + "ip": "123.123.123.123" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/trustx/trustxtest/supplemental/bad-status-500.json b/adapters/trustx/trustxtest/supplemental/bad-status-500.json new file mode 100644 index 00000000000..53e49da220d --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/bad-status-500.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "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/trustx/trustxtest/supplemental/invalid-bid-ext.json b/adapters/trustx/trustxtest/supplemental/invalid-bid-ext.json new file mode 100644 index 00000000000..17f20ad7317 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/invalid-bid-ext.json @@ -0,0 +1,133 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ua": "ua", + "ip": "123.123.123.123" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "uri": "https://test.localhost.com", + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "device": { + "ua": "ua", + "ip": "123.123.123.123" + }, + "app": { + "bundle": "app-bundle" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1, + "ext": { + "bidder": { + "trustx": { + "networkName": 1 + } + } + } + } + ] + } + ], + "seat": "trustx" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1, + "ext": { + "bidder": { + "trustx": { + "networkName": 1 + } + } + } + }, + "type": "banner", + "meta": {} + } + ], + "seat": "trustx" + } + ] +} diff --git a/adapters/trustx/trustxtest/supplemental/invalid-imp-ext.json b/adapters/trustx/trustxtest/supplemental/invalid-imp-ext.json new file mode 100644 index 00000000000..8260bae3012 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/invalid-imp-ext.json @@ -0,0 +1,122 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "tagid": "123", + "banner": { + "w": 300, + "h": 250 + }, + "ext": 1 + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index", + "ref": "https://example.com/page" + }, + "device": { + "ua": "ua", + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "test-domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + ], + "Referer": [ + "https://example.com/page" + ] + }, + "uri": "https://test.localhost.com", + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "tagid": "123", + "banner": { + "w": 300, + "h": 250 + }, + "ext": 1 + } + ], + "site": { + "domain": "test-domain.com", + "page": "http://test-domain.com/index", + "ref": "https://example.com/page" + }, + "device": { + "ua": "ua", + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1 + } + ] + } + ], + "seat": "trustx" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "price": 10, + "impid": "test-imp", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ], + "seat": "trustx" + } + ] +} diff --git a/adapters/trustx/trustxtest/supplemental/no-content-response.json b/adapters/trustx/trustxtest/supplemental/no-content-response.json new file mode 100644 index 00000000000..5d65dc0e738 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/no-content-response.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} + diff --git a/adapters/trustx/trustxtest/supplemental/test-response.json b/adapters/trustx/trustxtest/supplemental/test-response.json new file mode 100644 index 00000000000..3ee60f3ed57 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/test-response.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "test-request", + "test": 1, + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "test": 1, + "imp": [ + { + "id": "test-imp", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + } + ], + "seat": "trustx" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "crid": "4235d4b1-e2a6-9bbb-a34c-766714c2d221", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB3-8" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB3-8" + } + } + ] + } + ] +} + diff --git a/adapters/trustx/trustxtest/supplemental/unsupported-mtype.json b/adapters/trustx/trustxtest/supplemental/unsupported-mtype.json new file mode 100644 index 00000000000..0596f43cf54 --- /dev/null +++ b/adapters/trustx/trustxtest/supplemental/unsupported-mtype.json @@ -0,0 +1,122 @@ +{ + "mockBidRequest": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "ua" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ] + }, + "body": { + "id": "test-request", + "imp": [ + { + "id": "test-imp", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "uid": 1234 + } + } + } + ], + "app": { + "bundle": "app-bundle" + }, + "device": { + "ip": "123.123.123.123", + "ua": "ua" + } + }, + "impIDs": [ + "test-imp" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request", + "seatbid": [ + { + "bid": [ + { + "id": "2315f4a3-f2c4-8cac-69de-985272e1d706", + "impid": "test-imp", + "price": 10, + "adm": "test-ad", + "crid": "creative", + "w": 1920, + "h": 1080, + "mtype": 0, + "dur": 15 + } + ], + "seat": "trustx" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [] + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unsupported MType: 0", + "comparison": "literal" + } + ] +} + diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index e6bba140a57..ae9115ed395 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -240,6 +240,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/triplelift" "github.com/prebid/prebid-server/v3/adapters/triplelift_native" "github.com/prebid/prebid-server/v3/adapters/trustedstack" + "github.com/prebid/prebid-server/v3/adapters/trustx" "github.com/prebid/prebid-server/v3/adapters/ucfunnel" "github.com/prebid/prebid-server/v3/adapters/undertone" "github.com/prebid/prebid-server/v3/adapters/unicorn" @@ -513,6 +514,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderTriplelift: triplelift.Builder, openrtb_ext.BidderTripleliftNative: triplelift_native.Builder, openrtb_ext.BidderTrustedstack: trustedstack.Builder, + openrtb_ext.BidderTrustX: trustx.Builder, openrtb_ext.BidderUcfunnel: ucfunnel.Builder, openrtb_ext.BidderUndertone: undertone.Builder, openrtb_ext.BidderUnicorn: unicorn.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 82a6df4f77c..5396018460d 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -258,6 +258,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderTriplelift, BidderTripleliftNative, BidderTrustedstack, + BidderTrustX, BidderUcfunnel, BidderUndertone, BidderUnicorn, @@ -635,6 +636,7 @@ const ( BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" BidderTrustedstack BidderName = "trustedstack" + BidderTrustX BidderName = "trustx" BidderUcfunnel BidderName = "ucfunnel" BidderUndertone BidderName = "undertone" BidderUnicorn BidderName = "unicorn" diff --git a/openrtb_ext/imp_trustx.go b/openrtb_ext/imp_trustx.go new file mode 100644 index 00000000000..8187f91a2ce --- /dev/null +++ b/openrtb_ext/imp_trustx.go @@ -0,0 +1,9 @@ +package openrtb_ext + +import "encoding/json" + +// ExtImpTrustX defines the contract for bidrequest.imp[i].ext.prebid.bidder.trustx +type ExtImpTrustX struct { + Uid int `json:"uid"` + Keywords json.RawMessage `json:"keywords,omitempty"` +} diff --git a/static/bidder-info/trustx.yaml b/static/bidder-info/trustx.yaml index 1c60ac34c2f..878d1d1d23b 100644 --- a/static/bidder-info/trustx.yaml +++ b/static/bidder-info/trustx.yaml @@ -1,9 +1,19 @@ -aliasOf: grid +endpoint: "https://ads.trustx.org/pbs" maintainer: - email: "grid-tech@themediagrid.com" -gvlVendorID: 686 + email: "prebid@trustx.org" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video userSync: + iframe: + url: "https://static.cdn.trustx.org/x/user_sync.html?source=pbs&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}" + userMacro: "$UID" redirect: - url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}" - userMacro: "${BSW_UUID}" - \ No newline at end of file + url: "https://sync.trustx.org/usync-pbs?us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}" + userMacro: "$UID" diff --git a/static/bidder-params/trustx.json b/static/bidder-params/trustx.json new file mode 100644 index 00000000000..7560642aae6 --- /dev/null +++ b/static/bidder-params/trustx.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TRUSTX Adapter Params", + "description": "A schema which validates params accepted by the TRUSTX adapter", + "type": "object", + "properties": { + "uid": { + "type": "integer", + "description": "The ad slot id" + }, + "keywords": { + "type": "object", + "description": "Keywords" + } + } +}