diff --git a/adapters/rtbstack/params_test.go b/adapters/rtbstack/params_test.go new file mode 100644 index 00000000000..087302decb5 --- /dev/null +++ b/adapters/rtbstack/params_test.go @@ -0,0 +1,57 @@ +package rtbstack + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v4/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.BidderRTBStack, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected rtbstack params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderRTBStack, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"route":"https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145","tagId":"12345"}`, + `{"route":"https://site.eu-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2","tagId":"tag1"}`, + `{"route":"https://site.asia-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2","tagId":"tag1","customParams":{"foo":"bar"}}`, + `{"route":"https://example.us-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2","tagId":"tag1","customParams":{}}`, + `{"route":"https://example.us-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2","tagId":"tag1","customParams":{"nested":{"key":"value"}}}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"route":"https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2"}`, + `{"tagId":"12345"}`, + `{"route":"","tagId":"12345"}`, + `{"route":123,"tagId":"12345"}`, + `{"route":"https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2","tagId":123}`, +} diff --git a/adapters/rtbstack/rtbstack.go b/adapters/rtbstack/rtbstack.go new file mode 100644 index 00000000000..5b5b73f0a39 --- /dev/null +++ b/adapters/rtbstack/rtbstack.go @@ -0,0 +1,269 @@ +package rtbstack + +import ( + "fmt" + "net/http" + "net/url" + "strings" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v4/adapters" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/errortypes" + "github.com/prebid/prebid-server/v4/macros" + "github.com/prebid/prebid-server/v4/openrtb_ext" + "github.com/prebid/prebid-server/v4/util/jsonutil" +) + +type adapter struct { + endpoint *template.Template +} + +// impCtx represents the context containing an OpenRTB impression and its corresponding RTBStack extension configuration. +type impCtx struct { + imp openrtb2.Imp + rtbStackExt *openrtb_ext.ExtImpRTBStack +} + +// extImpRTBStack is used for imp->ext when sending to rtb-stack backend. +type extImpRTBStack struct { + CustomParams map[string]interface{} `json:"customParams,omitempty"` +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + tpl, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + bidder := &adapter{ + endpoint: tpl, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impressions in request", + }} + } + + var errs []error + impsByRoute := make(map[string][]*impCtx) + var routeOrder []string + + for i := range request.Imp { + imp, ext, err := preprocessImp(request.Imp[i]) + if err != nil { + errs = append(errs, err) + continue + } + + ctx := &impCtx{ + imp: imp, + rtbStackExt: ext, + } + if _, ok := impsByRoute[ext.Route]; !ok { + routeOrder = append(routeOrder, ext.Route) + } + impsByRoute[ext.Route] = append(impsByRoute[ext.Route], ctx) + } + + if len(routeOrder) == 0 { + return nil, errs + } + + var newSite *openrtb2.Site + if request.Site != nil && request.Site.Domain == "" { + siteCopy := *request.Site + pageURL, parseErr := url.Parse(request.Site.Page) + if parseErr == nil && pageURL.Hostname() != "" { + siteCopy.Domain = pageURL.Hostname() + } else { + siteCopy.Domain = request.Site.Page + } + newSite = &siteCopy + } + + requests := make([]*adapters.RequestData, 0, len(routeOrder)) + for _, route := range routeOrder { + group := impsByRoute[route] + + endpoint, err := a.buildEndpointURL(group[0].rtbStackExt) + if err != nil { + errs = append(errs, err) + continue + } + + processedImps := make([]openrtb2.Imp, 0, len(group)) + for _, v := range group { + processedImps = append(processedImps, v.imp) + } + + newRequest := *request + newRequest.Imp = processedImps + if newSite != nil { + newRequest.Site = newSite + } + + reqJSON, err := jsonutil.Marshal(newRequest) + if err != nil { + errs = append(errs, &errortypes.FailedToRequestBids{ + Message: "Error parsing reqJSON object", + }) + continue + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + requests = append(requests, &adapters.RequestData{ + Method: http.MethodPost, + Uri: endpoint, + Body: reqJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(newRequest.Imp), + }) + } + + if len(requests) == 0 { + return nil, errs + } + + return requests, 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 bidResp openrtb2.BidResponse + if err := jsonutil.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + if len(bidResp.SeatBid) == 0 { + return nil, nil + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp)) + + if bidResp.Cur != "" { + bidResponse.Currency = bidResp.Cur + } + + var bidErrs []error + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType, err := getMediaTypeForBid(sb.Bid[i]) + if err != nil { + bidErrs = append(bidErrs, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + }) + } + } + return bidResponse, bidErrs +} + +var validRegions = map[string]bool{"us": true, "eu": true, "sg": true} + +func (a *adapter) buildEndpointURL(ext *openrtb_ext.ExtImpRTBStack) (string, error) { + routeURL, err := url.Parse(ext.Route) + if err != nil { + return "", &errortypes.BadInput{Message: fmt.Sprintf("invalid route URL: %v", err)} + } + + region, err := extractRegion(routeURL.Hostname()) + if err != nil { + return "", err + } + + queryParams := routeURL.Query() + client := queryParams.Get("client") + endpoint := queryParams.Get("endpoint") + ssp := queryParams.Get("ssp") + + if client == "" || endpoint == "" || ssp == "" { + return "", &errortypes.BadInput{Message: "route URL must contain client, endpoint, and ssp query parameters"} + } + + params := macros.EndpointTemplateParams{ + Region: region, + SspID: ssp, + ZoneID: endpoint, + PartnerId: client, + } + + return macros.ResolveMacros(a.endpoint, params) +} + +func extractRegion(hostname string) (string, error) { + parts := strings.Split(hostname, ".") + for _, part := range parts { + if strings.HasSuffix(part, "-adx-admixer") { + region := strings.ToLower(strings.TrimSuffix(part, "-adx-admixer")) + if validRegions[region] { + return region, nil + } + } + } + return "", &errortypes.BadInput{Message: "unable to extract valid region from route URL hostname"} +} + +func preprocessImp( + imp openrtb2.Imp, +) (openrtb2.Imp, *openrtb_ext.ExtImpRTBStack, error) { + var bidderExt adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + return imp, nil, &errortypes.BadInput{Message: err.Error()} + } + + var impExt openrtb_ext.ExtImpRTBStack + if err := jsonutil.Unmarshal(bidderExt.Bidder, &impExt); err != nil { + return imp, nil, &errortypes.BadInput{ + Message: "Wrong RTBStack bidder ext", + } + } + + imp.TagID = impExt.TagId + + newExt := extImpRTBStack{ + CustomParams: impExt.CustomParams, + } + + newImpExtForRTBStack, err := jsonutil.Marshal(newExt) + if err != nil { + return imp, nil, &errortypes.BadInput{Message: err.Error()} + } + imp.Ext = newImpExtForRTBStack + + return imp, &impExt, nil +} + +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + case openrtb2.MarkupAudio: + return openrtb_ext.BidTypeAudio, nil + default: + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("unsupported MType %d for bid %s", bid.MType, bid.ImpID), + } + } +} diff --git a/adapters/rtbstack/rtbstack_test.go b/adapters/rtbstack/rtbstack_test.go new file mode 100644 index 00000000000..a28237d4caf --- /dev/null +++ b/adapters/rtbstack/rtbstack_test.go @@ -0,0 +1,29 @@ +package rtbstack + +import ( + "testing" + + "github.com/prebid/prebid-server/v4/adapters/adapterstest" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderRTBStack, config.Adapter{ + Endpoint: "https://{{.Region}}-adx-admixer.rtb-stack.com/pbs?ssp={{.SspID}}&endpoint={{.ZoneID}}&client={{.PartnerId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "rtbstacktest", bidder) +} + +func TestBuilderInvalidTemplate(t *testing.T) { + _, err := Builder(openrtb_ext.BidderRTBStack, config.Adapter{ + Endpoint: "https://{{.Region}-bad-template"}, config.Server{}) + + if err == nil { + t.Fatal("Builder should return an error for an invalid endpoint template") + } +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/app-audio.json b/adapters/rtbstack/rtbstacktest/exemplary/app-audio.json new file mode 100644 index 00000000000..437343b0563 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/app-audio.json @@ -0,0 +1,162 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-audio-short", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-audio-short", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-app-audio-long", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 15, + "maxduration": 60 + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-audio-long", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-audio-short", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 5, + "maxduration": 30 + }, + "tagid": "app-audio-short", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-app-audio-long", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 15, + "maxduration": 60 + }, + "tagid": "app-audio-long", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-app-audio-short", + "imp-app-audio-long" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-app-audio-short-1", + "impid": "imp-app-audio-short", + "price": 0.75, + "adm": "", + "crid": "crid-app-audio-short-1", + "mtype": 3 + }, + { + "id": "bid-app-audio-long-1", + "impid": "imp-app-audio-long", + "price": 1.25, + "adm": "", + "crid": "crid-app-audio-long-1", + "mtype": 3 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-app-audio-short-1", + "impid": "imp-app-audio-short", + "price": 0.75, + "adm": "", + "crid": "crid-app-audio-short-1", + "mtype": 3 + }, + "type": "audio" + }, + { + "bid": { + "id": "bid-app-audio-long-1", + "impid": "imp-app-audio-long", + "price": 1.25, + "adm": "", + "crid": "crid-app-audio-long-1", + "mtype": 3 + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/app-banner.json b/adapters/rtbstack/rtbstacktest/exemplary/app-banner.json new file mode 100644 index 00000000000..1072a3946bd --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/app-banner.json @@ -0,0 +1,162 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-basic", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-basic", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-app-large", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-basic", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "tagid": "app-basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-app-large", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "app-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-app-basic", + "imp-app-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-app-basic-1", + "impid": "imp-app-basic", + "price": 0.95, + "adm": "
App Banner 320x50
", + "crid": "crid-app-basic-1", + "mtype": 1 + }, + { + "id": "bid-app-large-1", + "impid": "imp-app-large", + "price": 1.5, + "adm": "
App Banner 728x90
", + "crid": "crid-app-large-1", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-app-basic-1", + "impid": "imp-app-basic", + "price": 0.95, + "adm": "
App Banner 320x50
", + "crid": "crid-app-basic-1", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-app-large-1", + "impid": "imp-app-large", + "price": 1.5, + "adm": "
App Banner 728x90
", + "crid": "crid-app-large-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/app-native.json b/adapters/rtbstack/rtbstacktest/exemplary/app-native.json new file mode 100644 index 00000000000..af5549ff6c4 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/app-native.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-native-small", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":50}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":120,\"hmin\":120,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":100}}]}" + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-native-small", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-app-native-large", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":2,\"adunit\":2,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":300,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":150}}]}" + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-native-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-native-small", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":50}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":120,\"hmin\":120,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":100}}]}" + }, + "tagid": "app-native-small", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-app-native-large", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":2,\"adunit\":2,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":300,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":150}}]}" + }, + "tagid": "app-native-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-app-native-small", + "imp-app-native-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-app-native-small-1", + "impid": "imp-app-native-small", + "price": 1.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Small App Native Ad\"}}]}}", + "crid": "crid-app-native-small-1", + "mtype": 4 + }, + { + "id": "bid-app-native-large-1", + "impid": "imp-app-native-large", + "price": 2.75, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Large App Native Ad\"}}]}}", + "crid": "crid-app-native-large-1", + "mtype": 4 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-app-native-small-1", + "impid": "imp-app-native-small", + "price": 1.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Small App Native Ad\"}}]}}", + "crid": "crid-app-native-small-1", + "mtype": 4 + }, + "type": "native" + }, + { + "bid": { + "id": "bid-app-native-large-1", + "impid": "imp-app-native-large", + "price": 2.75, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Large App Native Ad\"}}]}}", + "crid": "crid-app-native-large-1", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/app-video.json b/adapters/rtbstack/rtbstacktest/exemplary/app-video.json new file mode 100644 index 00000000000..c2d5b4e6bc6 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/app-video.json @@ -0,0 +1,188 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-video-small", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 5, + "maxduration": 30, + "protocols": [ + 2, + 5 + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-video-small", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-app-video-large", + "video": { + "w": 1280, + "h": 720, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 10, + "maxduration": 60, + "protocols": [ + 2, + 5, + 6 + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "app-video-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid.app" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-app-video-small", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 5, + "maxduration": 30, + "protocols": [ + 2, + 5 + ] + }, + "tagid": "app-video-small", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-app-video-large", + "video": { + "w": 1280, + "h": 720, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 10, + "maxduration": 60, + "protocols": [ + 2, + 5, + 6 + ] + }, + "tagid": "app-video-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-app-video-small", + "imp-app-video-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-app-video-small-1", + "impid": "imp-app-video-small", + "price": 3.0, + "adm": "App Small Video Ad", + "crid": "crid-app-video-small-1", + "mtype": 2 + }, + { + "id": "bid-app-video-large-1", + "impid": "imp-app-video-large", + "price": 5.5, + "adm": "App Large Video Ad", + "crid": "crid-app-video-large-1", + "mtype": 2 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-app-video-small-1", + "impid": "imp-app-video-small", + "price": 3.0, + "adm": "App Small Video Ad", + "crid": "crid-app-video-small-1", + "mtype": 2 + }, + "type": "video" + }, + { + "bid": { + "id": "bid-app-video-large-1", + "impid": "imp-app-video-large", + "price": 5.5, + "adm": "App Large Video Ad", + "crid": "crid-app-video-large-1", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/multi-route-grouping.json b/adapters/rtbstack/rtbstacktest/exemplary/multi-route-grouping.json new file mode 100644 index 00000000000..543b99815e1 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/multi-route-grouping.json @@ -0,0 +1,235 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-route-a-1", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=client-a&endpoint=100&ssp=200", + "tagId": "tag-a-1" + } + } + }, + { + "id": "imp-route-b-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.eu-adx-admixer.rtb-stack.com/prebid?client=client-b&endpoint=300&ssp=400", + "tagId": "tag-b-1" + } + } + }, + { + "id": "imp-route-a-2", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=client-a&endpoint=100&ssp=200", + "tagId": "tag-a-2" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=200&endpoint=100&client=client-a", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-route-a-1", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "tag-a-1", + "ext": {} + }, + { + "id": "imp-route-a-2", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "tagid": "tag-a-2", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-route-a-1", + "imp-route-a-2" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-a-1", + "impid": "imp-route-a-1", + "price": 1.25, + "adm": "
A1
", + "crid": "crid-a-1", + "mtype": 1 + }, + { + "id": "bid-a-2", + "impid": "imp-route-a-2", + "price": 2.5, + "adm": "
A2
", + "crid": "crid-a-2", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + }, + { + "expectedRequest": { + "uri": "https://eu-adx-admixer.rtb-stack.com/pbs?ssp=400&endpoint=300&client=client-b", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-route-b-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "tag-b-1", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-route-b-1" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-b-1", + "impid": "imp-route-b-1", + "price": 0.75, + "adm": "
B1
", + "crid": "crid-b-1", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-a-1", + "impid": "imp-route-a-1", + "price": 1.25, + "adm": "
A1
", + "crid": "crid-a-1", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-a-2", + "impid": "imp-route-a-2", + "price": 2.5, + "adm": "
A2
", + "crid": "crid-a-2", + "mtype": 1 + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-b-1", + "impid": "imp-route-b-1", + "price": 0.75, + "adm": "
B1
", + "crid": "crid-b-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/multi-route-same.json b/adapters/rtbstack/rtbstacktest/exemplary/multi-route-same.json new file mode 100644 index 00000000000..a419ed2bccc --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/multi-route-same.json @@ -0,0 +1,162 @@ +{ + "mockBidRequest": { + "id": "test-request-same-route", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-same-1", + "banner": { + "format": [{ "w": 728, "h": 90 }] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=client-x&endpoint=500&ssp=600", + "tagId": "tag-1" + } + } + }, + { + "id": "imp-same-2", + "banner": { + "format": [{ "w": 300, "h": 250 }] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=client-x&endpoint=500&ssp=600", + "tagId": "tag-2" + } + } + }, + { + "id": "imp-same-3", + "banner": { + "format": [{ "w": 970, "h": 250 }] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=client-x&endpoint=500&ssp=600", + "tagId": "tag-3" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=600&endpoint=500&client=client-x", + "body": { + "id": "test-request-same-route", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-same-1", + "banner": { "format": [{ "w": 728, "h": 90 }] }, + "tagid": "tag-1", + "ext": {} + }, + { + "id": "imp-same-2", + "banner": { "format": [{ "w": 300, "h": 250 }] }, + "tagid": "tag-2", + "ext": {} + }, + { + "id": "imp-same-3", + "banner": { "format": [{ "w": 970, "h": 250 }] }, + "tagid": "tag-3", + "ext": {} + } + ] + }, + "impIDs": ["imp-same-1", "imp-same-2", "imp-same-3"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-same-route", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-same-1", + "impid": "imp-same-1", + "price": 1.10, + "adm": "
1
", + "crid": "crid-1", + "mtype": 1 + }, + { + "id": "bid-same-2", + "impid": "imp-same-2", + "price": 1.20, + "adm": "
2
", + "crid": "crid-2", + "mtype": 1 + }, + { + "id": "bid-same-3", + "impid": "imp-same-3", + "price": 1.30, + "adm": "
3
", + "crid": "crid-3", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-same-1", + "impid": "imp-same-1", + "price": 1.10, + "adm": "
1
", + "crid": "crid-1", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-same-2", + "impid": "imp-same-2", + "price": 1.20, + "adm": "
2
", + "crid": "crid-2", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-same-3", + "impid": "imp-same-3", + "price": 1.30, + "adm": "
3
", + "crid": "crid-3", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/no-currency-in-response.json b/adapters/rtbstack/rtbstacktest/exemplary/no-currency-in-response.json new file mode 100644 index 00000000000..8e3f9494605 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/no-currency-in-response.json @@ -0,0 +1,99 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "test-tag" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "test-tag", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-banner" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-1", + "impid": "imp-banner", + "price": 1.0, + "adm": "
Banner
", + "crid": "crid-1", + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-banner", + "price": 1.0, + "adm": "
Banner
", + "crid": "crid-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/optional-params.json b/adapters/rtbstack/rtbstacktest/exemplary/optional-params.json new file mode 100644 index 00000000000..8e3f14abfd9 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/optional-params.json @@ -0,0 +1,228 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-empty-custom", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "empty-custom", + "customParams": {} + } + } + }, + { + "id": "imp-numeric-bool", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "numeric-bool", + "customParams": { + "floor": 0.45, + "enabled": true, + "count": 3 + } + } + } + }, + { + "id": "imp-nested-custom", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "nested", + "customParams": { + "placement": { + "position": "top", + "sticky": true + }, + "sizes": [ + "970x250", + "728x90" + ] + } + } + } + }, + { + "id": "imp-no-custom", + "banner": { + "format": [ + { + "w": 468, + "h": 60 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "no-custom" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-empty-custom", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "empty-custom", + "ext": {} + }, + { + "id": "imp-numeric-bool", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "tagid": "numeric-bool", + "ext": { + "customParams": { + "floor": 0.45, + "enabled": true, + "count": 3 + } + } + }, + { + "id": "imp-nested-custom", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "tagid": "nested", + "ext": { + "customParams": { + "placement": { + "position": "top", + "sticky": true + }, + "sizes": [ + "970x250", + "728x90" + ] + } + } + }, + { + "id": "imp-no-custom", + "banner": { + "format": [ + { + "w": 468, + "h": 60 + } + ] + }, + "tagid": "no-custom", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-basic", + "imp-empty-custom", + "imp-numeric-bool", + "imp-nested-custom", + "imp-no-custom" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-audio.json b/adapters/rtbstack/rtbstacktest/exemplary/site-audio.json new file mode 100644 index 00000000000..952d026e80b --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-audio.json @@ -0,0 +1,158 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-audio-small", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "site-audio-small", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-audio-large", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 15, + "maxduration": 60 + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "site-audio-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-audio-small", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 5, + "maxduration": 30 + }, + "tagid": "site-audio-small", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-audio-large", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp4" + ], + "minduration": 15, + "maxduration": 60 + }, + "tagid": "site-audio-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-audio-small", + "imp-audio-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-site-audio-small-1", + "impid": "imp-audio-small", + "price": 0.75, + "adm": "", + "crid": "crid-site-audio-small-1", + "mtype": 3 + }, + { + "id": "bid-site-audio-large-1", + "impid": "imp-audio-large", + "price": 1.25, + "adm": "", + "crid": "crid-site-audio-large-1", + "mtype": 3 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-site-audio-small-1", + "impid": "imp-audio-small", + "price": 0.75, + "adm": "", + "crid": "crid-site-audio-small-1", + "mtype": 3 + }, + "type": "audio" + }, + { + "bid": { + "id": "bid-site-audio-large-1", + "impid": "imp-audio-large", + "price": 1.25, + "adm": "", + "crid": "crid-site-audio-large-1", + "mtype": 3 + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-banner.json b/adapters/rtbstack/rtbstacktest/exemplary/site-banner.json new file mode 100644 index 00000000000..2c0e908c6e3 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-banner.json @@ -0,0 +1,229 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-empty-custom", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "empty-custom", + "customParams": {} + } + } + }, + { + "id": "imp-nested-custom", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "nested", + "customParams": { + "placement": { + "position": "top", + "sticky": true + }, + "sizes": [ + "970x250", + "728x90" + ] + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-empty-custom", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "empty-custom", + "ext": {} + }, + { + "id": "imp-nested-custom", + "banner": { + "format": [ + { + "w": 970, + "h": 250 + } + ] + }, + "tagid": "nested", + "ext": { + "customParams": { + "placement": { + "position": "top", + "sticky": true + }, + "sizes": [ + "970x250", + "728x90" + ] + } + } + } + ] + }, + "impIDs": [ + "imp-basic", + "imp-empty-custom", + "imp-nested-custom" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-basic-1", + "impid": "imp-basic", + "price": 1.25, + "adm": "
Banner 728x90
", + "crid": "crid-basic-1", + "mtype": 1 + }, + { + "id": "bid-empty-1", + "impid": "imp-empty-custom", + "price": 0.75, + "adm": "
Banner 300x250
", + "crid": "crid-empty-1", + "mtype": 1 + }, + { + "id": "bid-nested-1", + "impid": "imp-nested-custom", + "price": 2.5, + "adm": "
Banner 970x250
", + "crid": "crid-nested-1", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-basic-1", + "impid": "imp-basic", + "price": 1.25, + "adm": "
Banner 728x90
", + "crid": "crid-basic-1", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-empty-1", + "impid": "imp-empty-custom", + "price": 0.75, + "adm": "
Banner 300x250
", + "crid": "crid-empty-1", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-nested-1", + "impid": "imp-nested-custom", + "price": 2.5, + "adm": "
Banner 970x250
", + "crid": "crid-nested-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-domain-empty-mutation.json b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-empty-mutation.json new file mode 100644 index 00000000000..4339d77473e --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-empty-mutation.json @@ -0,0 +1,70 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "imp-basic" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-domain-fallback.json b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-fallback.json new file mode 100644 index 00000000000..fd198b1bc29 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-fallback.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "not-a-url", + "domain": "" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "test-tag" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "not-a-url", + "domain": "not-a-url" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "test-tag", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-banner" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-1", + "impid": "imp-banner", + "price": 1.0, + "adm": "
Banner
", + "crid": "crid-1", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-banner", + "price": 1.0, + "adm": "
Banner
", + "crid": "crid-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-domain-mutation.json b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-mutation.json new file mode 100644 index 00000000000..89954b712a9 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-domain-mutation.json @@ -0,0 +1,71 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://not-empty.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://not-empty.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "imp-basic" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-native.json b/adapters/rtbstack/rtbstacktest/exemplary/site-native.json new file mode 100644 index 00000000000..9d4b0182030 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-native.json @@ -0,0 +1,148 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-site-native-small", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":50}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":120,\"hmin\":120,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":100}}]}" + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "site-native-small", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-site-native-large", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":2,\"adunit\":2,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":300,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":150}}]}" + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "site-native-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "device": { + "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "imp-site-native-small", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":1,\"adunit\":1,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":50}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":120,\"hmin\":120,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":100}}]}" + }, + "tagid": "site-native-small", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-site-native-large", + "native": { + "ver": "1.1", + "request": "{\"ver\":\"1.0\",\"layout\":2,\"adunit\":2,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":300,\"hmin\":300,\"mimes\":[\"image/jpeg\",\"image/png\"]}},{\"id\":3,\"required\":0,\"data\":{\"type\":2,\"len\":150}}]}" + }, + "tagid": "site-native-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-site-native-small", + "imp-site-native-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-site-native-small-1", + "impid": "imp-site-native-small", + "price": 1.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Small Native Ad\"}}]}}", + "crid": "crid-site-native-small-1", + "mtype": 4 + }, + { + "id": "bid-site-native-large-1", + "impid": "imp-site-native-large", + "price": 2.75, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Large Native Ad\"}}]}}", + "crid": "crid-site-native-large-1", + "mtype": 4 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-site-native-small-1", + "impid": "imp-site-native-small", + "price": 1.5, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Small Native Ad\"}}]}}", + "crid": "crid-site-native-small-1", + "mtype": 4 + }, + "type": "native" + }, + { + "bid": { + "id": "bid-site-native-large-1", + "impid": "imp-site-native-large", + "price": 2.75, + "adm": "{\"native\":{\"assets\":[{\"id\":1,\"title\":{\"text\":\"Large Native Ad\"}}]}}", + "crid": "crid-site-native-large-1", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-region-eu.json b/adapters/rtbstack/rtbstacktest/exemplary/site-region-eu.json new file mode 100644 index 00000000000..c027397c7f2 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-region-eu.json @@ -0,0 +1,107 @@ +{ + "mockBidRequest": { + "id": "test-request-eu", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-eu-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://publisher.eu-adx-admixer.rtb-stack.com/prebid?client=a1b2c3d4-e5f6-7890-abcd-ef1234567890&endpoint=500&ssp=200", + "tagId": "eu-banner", + "customParams": { + "geo": "europe" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://eu-adx-admixer.rtb-stack.com/pbs?ssp=200&endpoint=500&client=a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "body": { + "id": "test-request-eu", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-eu-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "eu-banner", + "ext": { + "customParams": { + "geo": "europe" + } + } + } + ] + }, + "impIDs": [ + "imp-eu-banner" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-eu", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-eu-1", + "impid": "imp-eu-banner", + "price": 1.1, + "adm": "
EU Banner 300x250
", + "crid": "crid-eu-1", + "mtype": 1 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "bid-eu-1", + "impid": "imp-eu-banner", + "price": 1.1, + "adm": "
EU Banner 300x250
", + "crid": "crid-eu-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-region-sg.json b/adapters/rtbstack/rtbstacktest/exemplary/site-region-sg.json new file mode 100644 index 00000000000..7c685fe00ab --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-region-sg.json @@ -0,0 +1,113 @@ +{ + "mockBidRequest": { + "id": "test-request-sg", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-sg-video", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ] + }, + "ext": { + "bidder": { + "route": "https://mysite.sg-adx-admixer.rtb-stack.com/prebid?client=f9e8d7c6-b5a4-3210-fedc-ba0987654321&endpoint=777&ssp=88", + "tagId": "sg-video", + "customParams": { + "geo": "asia" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://sg-adx-admixer.rtb-stack.com/pbs?ssp=88&endpoint=777&client=f9e8d7c6-b5a4-3210-fedc-ba0987654321", + "body": { + "id": "test-request-sg", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-sg-video", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ] + }, + "tagid": "sg-video", + "ext": { + "customParams": { + "geo": "asia" + } + } + } + ] + }, + "impIDs": [ + "imp-sg-video" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-sg", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-sg-1", + "impid": "imp-sg-video", + "price": 4.5, + "adm": "SG Video Ad", + "crid": "crid-sg-1", + "mtype": 2 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-sg-1", + "impid": "imp-sg-video", + "price": 4.5, + "adm": "SG Video Ad", + "crid": "crid-sg-1", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/exemplary/site-video.json b/adapters/rtbstack/rtbstacktest/exemplary/site-video.json new file mode 100644 index 00000000000..6d3f1aeefe0 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/exemplary/site-video.json @@ -0,0 +1,184 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-video-small", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 5, + "maxduration": 30, + "protocols": [ + 2, + 5 + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "video-small", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-video-large", + "video": { + "w": 1280, + "h": 720, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 10, + "maxduration": 60, + "protocols": [ + 2, + 5, + 6 + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "video-large", + "customParams": {} + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-video-small", + "video": { + "w": 640, + "h": 360, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 5, + "maxduration": 30, + "protocols": [ + 2, + 5 + ] + }, + "tagid": "video-small", + "ext": { + "customParams": { + "foo": "bar" + } + } + }, + { + "id": "imp-video-large", + "video": { + "w": 1280, + "h": 720, + "mimes": [ + "video/mp4", + "video/webm" + ], + "minduration": 10, + "maxduration": 60, + "protocols": [ + 2, + 5, + 6 + ] + }, + "tagid": "video-large", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-video-small", + "imp-video-large" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-video-small-1", + "impid": "imp-video-small", + "price": 3.0, + "adm": "Small Video Ad", + "crid": "crid-video-small-1", + "mtype": 2 + }, + { + "id": "bid-video-large-1", + "impid": "imp-video-large", + "price": 5.5, + "adm": "Large Video Ad", + "crid": "crid-video-large-1", + "mtype": 2 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-video-small-1", + "impid": "imp-video-small", + "price": 3.0, + "adm": "Small Video Ad", + "crid": "crid-video-small-1", + "mtype": 2 + }, + "type": "video" + }, + { + "bid": { + "id": "bid-video-large-1", + "impid": "imp-video-large", + "price": 5.5, + "adm": "Large Video Ad", + "crid": "crid-video-large-1", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-bidder-ext.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-bidder-ext.json new file mode 100644 index 00000000000..5d3c2f45095 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-bidder-ext.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "test-request-bad-bidder-ext", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-bad-ext", + "banner": { + "format": [{ "w": 300, "h": 250 }] + }, + "ext": { + "bidder": "not-an-object" + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "Wrong RTBStack bidder ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-imp.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-imp.json new file mode 100644 index 00000000000..7fb55166c3f --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-imp.json @@ -0,0 +1,29 @@ +{ + "mockBidRequest": { + "id": "test-request-error", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-invalid", + "banner": { + "format": [{ "w": 300, "h": 250 }] + }, + "ext": "invalid-json" + } + ] + }, + + "httpCalls": [], + + "expectedMakeRequestsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-mtype.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-mtype.json new file mode 100644 index 00000000000..2b79830d114 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-mtype.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "test-tag" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "test-tag", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-banner" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-1", + "impid": "imp-banner", + "price": 1.0, + "adm": "
Banner
", + "crid": "crid-1", + "mtype": 0 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [] + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unsupported MType 0 for bid imp-banner", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-response.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-response.json new file mode 100644 index 00000000000..2901ee1359a --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-response.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "test-banner", + "customParams": { + "foo": "bar" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "test-banner", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": "{\"id\"data.lost" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-route-invalid-region.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-route-invalid-region.json new file mode 100644 index 00000000000..156148b2cd9 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-route-invalid-region.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-bad-region", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-bad-region", + "banner": { + "format": [{ "w": 300, "h": 250 }] + }, + "ext": { + "bidder": { + "route": "https://publisher.xx-adx-admixer.rtb-stack.com/prebid?client=abc&endpoint=1&ssp=2", + "tagId": "test-tag" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "unable to extract valid region from route URL hostname", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/bad-route-missing-params.json b/adapters/rtbstack/rtbstacktest/supplemental/bad-route-missing-params.json new file mode 100644 index 00000000000..ad6a6a463dc --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/bad-route-missing-params.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-missing-params", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-missing-params", + "banner": { + "format": [{ "w": 300, "h": 250 }] + }, + "ext": { + "bidder": { + "route": "https://publisher.us-adx-admixer.rtb-stack.com/prebid", + "tagId": "test-tag" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "route URL must contain client, endpoint, and ssp query parameters", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/multi-route-partial-failure.json b/adapters/rtbstack/rtbstacktest/supplemental/multi-route-partial-failure.json new file mode 100644 index 00000000000..ebbf7c392d1 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/multi-route-partial-failure.json @@ -0,0 +1,123 @@ +{ + "mockBidRequest": { + "id": "test-request-mixed-routes", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-good", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://publisher.us-adx-admixer.rtb-stack.com/prebid?client=client-good&endpoint=10&ssp=20", + "tagId": "good-tag" + } + } + }, + { + "id": "imp-bad-region", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "route": "https://publisher.xx-adx-admixer.rtb-stack.com/prebid?client=client-bad&endpoint=99&ssp=88", + "tagId": "bad-tag" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=20&endpoint=10&client=client-good", + "body": { + "id": "test-request-mixed-routes", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [ + { + "id": "imp-good", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "good-tag", + "ext": {} + } + ] + }, + "impIDs": [ + "imp-good" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-mixed-routes", + "seatbid": [ + { + "seat": "rtbstack", + "bid": [ + { + "id": "bid-good-1", + "impid": "imp-good", + "price": 1.0, + "adm": "
good
", + "crid": "crid-good-1", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-good-1", + "impid": "imp-good", + "price": 1.0, + "adm": "
good
", + "crid": "crid-good-1", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "unable to extract valid region from route URL hostname", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/no-imps.json b/adapters/rtbstack/rtbstacktest/supplemental/no-imps.json new file mode 100644 index 00000000000..74130ada649 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/no-imps.json @@ -0,0 +1,17 @@ +{ + "mockBidRequest": { + "id": "test-request-no-imps", + "site": { + "page": "https://example.com", + "domain": "example.com" + }, + "imp": [] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "No impressions in request", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/status-204.json b/adapters/rtbstack/rtbstacktest/supplemental/status-204.json new file mode 100644 index 00000000000..49d97f6c639 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/status-204.json @@ -0,0 +1,71 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "imp-basic" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/status-400.json b/adapters/rtbstack/rtbstacktest/supplemental/status-400.json new file mode 100644 index 00000000000..4b8f01fd8a5 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/status-400.json @@ -0,0 +1,77 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "basic", + "customParams": { + "foo": "bar" + } + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-basic", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "basic", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "imp-basic" + ] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/rtbstack/rtbstacktest/supplemental/two-imps-one-is-bad.json b/adapters/rtbstack/rtbstacktest/supplemental/two-imps-one-is-bad.json new file mode 100644 index 00000000000..04c1d35d984 --- /dev/null +++ b/adapters/rtbstack/rtbstacktest/supplemental/two-imps-one-is-bad.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-partial", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-valid", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "route": "https://testsite.us-adx-admixer.rtb-stack.com/prebid?client=c4527281-5aa5-4c8e-bc53-a80bb3f99470&endpoint=309&ssp=145", + "tagId": "valid-tag", + "customParams": { + "foo": "bar" + } + } + } + }, + { + "id": "imp-invalid", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": "invalid-json" + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx-admixer.rtb-stack.com/pbs?ssp=145&endpoint=309&client=c4527281-5aa5-4c8e-bc53-a80bb3f99470", + "body": { + "id": "test-request-partial", + "site": { + "page": "https://example.com", + "domain": "https://example.com" + }, + "imp": [ + { + "id": "imp-valid", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "tagid": "valid-tag", + "ext": { + "customParams": { + "foo": "bar" + } + } + } + ] + }, + "impIDs": [ + "imp-valid" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ], + "expectedBidResponses": [] +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 630fbe348b9..a9a93f172dc 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -199,6 +199,7 @@ import ( "github.com/prebid/prebid-server/v4/adapters/rise" "github.com/prebid/prebid-server/v4/adapters/roulax" "github.com/prebid/prebid-server/v4/adapters/rtbhouse" + "github.com/prebid/prebid-server/v4/adapters/rtbstack" "github.com/prebid/prebid-server/v4/adapters/rubicon" salunamedia "github.com/prebid/prebid-server/v4/adapters/sa_lunamedia" "github.com/prebid/prebid-server/v4/adapters/seedingAlliance" @@ -469,6 +470,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderRise: rise.Builder, openrtb_ext.BidderRoulax: roulax.Builder, openrtb_ext.BidderRTBHouse: rtbhouse.Builder, + openrtb_ext.BidderRTBStack: rtbstack.Builder, openrtb_ext.BidderRubicon: rubicon.Builder, openrtb_ext.BidderSeedingAlliance: seedingAlliance.Builder, openrtb_ext.BidderSeedtag: seedtag.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 1b61ff724e2..fb58b51b58b 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -217,6 +217,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderRise, BidderRoulax, BidderRTBHouse, + BidderRTBStack, BidderRubicon, BidderSeedingAlliance, BidderSeedtag, @@ -591,6 +592,7 @@ const ( BidderRise BidderName = "rise" BidderRoulax BidderName = "roulax" BidderRTBHouse BidderName = "rtbhouse" + BidderRTBStack BidderName = "rtbstack" BidderRubicon BidderName = "rubicon" BidderSeedingAlliance BidderName = "seedingAlliance" BidderSeedtag BidderName = "seedtag" diff --git a/openrtb_ext/imp_rtbstack.go b/openrtb_ext/imp_rtbstack.go new file mode 100644 index 00000000000..d98e1b69bdd --- /dev/null +++ b/openrtb_ext/imp_rtbstack.go @@ -0,0 +1,7 @@ +package openrtb_ext + +type ExtImpRTBStack struct { + Route string `json:"route"` + TagId string `json:"tagId"` + CustomParams map[string]interface{} `json:"customParams,omitempty"` +} diff --git a/static/bidder-info/rtbstack.yaml b/static/bidder-info/rtbstack.yaml new file mode 100644 index 00000000000..34f02f0e973 --- /dev/null +++ b/static/bidder-info/rtbstack.yaml @@ -0,0 +1,19 @@ +endpoint: "https://{{.Region}}-adx-admixer.rtb-stack.com/pbs?ssp={{.SspID}}&endpoint={{.ZoneID}}&client={{.PartnerId}}" +maintainer: + email: "prebid@admixer.net" +gvlVendorID: 511 +openrtb: + version: "2.6" +capabilities: + site: + mediaTypes: + - banner + - video + - native + - audio + app: + mediaTypes: + - banner + - video + - native + - audio \ No newline at end of file diff --git a/static/bidder-params/rtbstack.json b/static/bidder-params/rtbstack.json new file mode 100644 index 00000000000..b5b90aefd06 --- /dev/null +++ b/static/bidder-params/rtbstack.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "RTBStack Adapter Params", + "description": "A schema which validates params accepted by the RTBStack adapter", + + "type": "object", + "properties": { + "route": { + "type": "string", + "minLength": 1, + "description": "Full route URL used to derive RTBStack endpoint (contains region, client, endpoint, ssp)." + }, + "tagId": { + "type": "string", + "description": "AdUnit tag id." + }, + "customParams": { + "type": "object", + "description": "Custom values for targeting." + } + }, + + "required": ["route", "tagId"] +} \ No newline at end of file