diff --git a/adapters/peak226/params_test.go b/adapters/peak226/params_test.go new file mode 100644 index 00000000000..86a99437ce9 --- /dev/null +++ b/adapters/peak226/params_test.go @@ -0,0 +1,59 @@ +package peak226 + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v4/openrtb_ext" +) + +var validParams = []string{ + `{ "publisherId": "pub-123", "placementId": "plc-456" }`, + `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "us" }`, + `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "eu" }`, + `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "jp" }`, +} + +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.BidderPeak226, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Peak226 params: %s\n Error: %s", validParam, err) + } + } +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{ "placementId": "plc-456" }`, + `{ "publisherId": "pub-123" }`, + `{ "publisherId": "", "placementId": "plc-456" }`, + `{ "publisherId": "pub-123", "placementId": "" }`, + `{ "publisherId": 123, "placementId": "plc-456" }`, + `{ "publisherId": "pub-123", "placementId": 456 }`, + `{ "publisherId": "pub-123", "placementId": "plc-456", "region": "asia" }`, + `{ "publisherId": "pub-123", "placementId": "plc-456", "region": 1 }`, +} + +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.BidderPeak226, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} diff --git a/adapters/peak226/peak226.go b/adapters/peak226/peak226.go new file mode 100644 index 00000000000..5562ad9fc27 --- /dev/null +++ b/adapters/peak226/peak226.go @@ -0,0 +1,303 @@ +package peak226 + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "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" +) + +const ( + defaultRegion = "us" + currencyUSD = "USD" + // zeroIFA is the sentinel value the OS reports for device.ifa when the user has not + // granted app tracking permission (e.g. iOS ATT declined). It is not a real device ID. + zeroIFA = "00000000-0000-0000-0000-000000000000" +) + +type adapter struct { + endpoint *template.Template +} + +// Builder builds a new instance of the Peak226 adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + endpointTemplate, 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: endpointTemplate, + } + return bidder, nil +} + +// regionPublisher groups impressions by both region and publisher ID, since each is a +// distinct request-level value (endpoint region, site/app.publisher.id) and impressions +// with different publisherId values must not be merged into the same outgoing request. +type regionPublisher struct { + region string + publisherID string +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + + impGroups := make(map[regionPublisher][]openrtb2.Imp) + var groupOrder []regionPublisher + + for _, imp := range request.Imp { + var bidderExt adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()), + }) + continue + } + + var peak226Ext openrtb_ext.ImpExtPeak226 + if err := jsonutil.Unmarshal(bidderExt.Bidder, &peak226Ext); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()), + }) + continue + } + + imp.TagID = peak226Ext.PlacementID + + if err := stripBidderExt(&imp); err != nil { + errs = append(errs, &errortypes.BadInput{ + Message: fmt.Sprintf("imp #%s: %s", imp.ID, err.Error()), + }) + continue + } + + if imp.BidFloor > 0 && imp.BidFloorCur != "" && !strings.EqualFold(imp.BidFloorCur, currencyUSD) { + convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, currencyUSD) + if err != nil { + errs = append(errs, err) + continue + } + imp.BidFloor = convertedValue + imp.BidFloorCur = currencyUSD + } + + region := peak226Ext.Region + if region == "" { + region = defaultRegion + } + + key := regionPublisher{region: region, publisherID: peak226Ext.PublisherID} + if _, ok := impGroups[key]; !ok { + groupOrder = append(groupOrder, key) + } + impGroups[key] = append(impGroups[key], imp) + } + + if len(groupOrder) == 0 { + return nil, errs + } + + device := sanitizeDevice(request.Device) + requests := make([]*adapters.RequestData, 0, len(groupOrder)) + + for _, key := range groupOrder { + imps := impGroups[key] + + requestCopy := *request + requestCopy.Imp = imps + requestCopy.Device = device + setPublisherID(&requestCopy, key.publisherID) + + endpoint, err := macros.ResolveMacros(a.endpoint, macros.EndpointTemplateParams{Region: key.region}) + if err != nil { + errs = append(errs, err) + continue + } + + requestJSON, err := jsonutil.Marshal(&requestCopy) + if err != nil { + errs = append(errs, err) + 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: requestJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp), + }) + } + + if len(requests) == 0 { + return nil, errs + } + + return requests, errs +} + +// stripBidderExt removes only the "bidder" key from imp.ext, preserving non-bidder +// signals such as gpid, data and tid that the Prebid.js adapter also forwards. When +// nothing else remains, imp.Ext is cleared so the imp serializes without an empty "ext". +func stripBidderExt(imp *openrtb2.Imp) error { + if len(imp.Ext) == 0 { + imp.Ext = nil + return nil + } + + var ext map[string]json.RawMessage + if err := jsonutil.Unmarshal(imp.Ext, &ext); err != nil { + return err + } + + delete(ext, "bidder") + + if len(ext) == 0 { + imp.Ext = nil + return nil + } + + updatedExt, err := jsonutil.Marshal(ext) + if err != nil { + return err + } + imp.Ext = updatedExt + + return nil +} + +// setPublisherID mirrors the Prebid.js adapter's behavior of writing the publisherId +// param onto app.publisher.id when the request is an app request, or site.publisher.id otherwise. +func setPublisherID(request *openrtb2.BidRequest, publisherID string) { + if publisherID == "" { + return + } + + if request.App != nil { + appCopy := *request.App + appCopy.Publisher = clonePublisher(appCopy.Publisher, publisherID) + request.App = &appCopy + return + } + + var siteCopy openrtb2.Site + if request.Site != nil { + siteCopy = *request.Site + } + siteCopy.Publisher = clonePublisher(siteCopy.Publisher, publisherID) + request.Site = &siteCopy +} + +// sanitizeDevice clears device.ifa when it's the all-zero sentinel value reported by the +// OS when app tracking permission was declined, so it's never forwarded as if it were a +// real device ID. There is no cookie-sync equivalent for app traffic; device.ifa and +// user.eids (from an in-app ID SDK, if the app integrates one) are the identity signals +// available for app-originated requests, and both already pass through unmodified otherwise. +func sanitizeDevice(device *openrtb2.Device) *openrtb2.Device { + if device == nil || device.IFA != zeroIFA { + return device + } + deviceCopy := *device + deviceCopy.IFA = "" + return &deviceCopy +} + +func clonePublisher(publisher *openrtb2.Publisher, id string) *openrtb2.Publisher { + if publisher == nil { + return &openrtb2.Publisher{ID: id} + } + publisherCopy := *publisher + publisherCopy.ID = id + return &publisherCopy +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *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{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("bad server response: %s", err.Error()), + }} + } + + if len(bidResp.SeatBid) == 0 { + return adapters.NewBidderResponse(), nil + } + + var errs []error + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + if bidResp.Cur != "" { + bidderResponse.Currency = bidResp.Cur + } + + for _, seatBid := range bidResp.SeatBid { + for i := range seatBid.Bid { + bid := seatBid.Bid[i] + + bidType, err := getMediaTypeForBid(bid) + if err != nil { + errs = append(errs, err) + continue + } + + resolveMacros(&bid) + + bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } + } + + return bidderResponse, errs +} + +// resolveMacros substitutes the OpenRTB ${AUCTION_PRICE} macro in adm and nurl with the +// bid price. peak226 always returns the macro in adm and relies on the demand-side adapter +// to expand it, so leaving it unresolved would render the literal macro text in the creative +// and report the wrong price on the win notice. +func resolveMacros(bid *openrtb2.Bid) { + if bid == nil { + return + } + price := strconv.FormatFloat(bid.Price, 'f', -1, 64) + bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1) + bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1) +} + +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 + } + + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("unrecognized bid type for impression %s", bid.ImpID), + } +} diff --git a/adapters/peak226/peak226_test.go b/adapters/peak226/peak226_test.go new file mode 100644 index 00000000000..0176879ec71 --- /dev/null +++ b/adapters/peak226/peak226_test.go @@ -0,0 +1,97 @@ +package peak226 + +import ( + "encoding/json" + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + + "github.com/prebid/prebid-server/v4/adapters/adapterstest" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderPeak226, config.Adapter{ + Endpoint: "https://{{.Region}}.a.viddea.com/edge_direct"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "peak226test", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderPeak226, config.Adapter{ + Endpoint: "{{Malformed}}"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} + +func TestStripBidderExt(t *testing.T) { + testCases := []struct { + name string + impExt json.RawMessage + expectedExt json.RawMessage + expectError bool + }{ + { + name: "nil ext is left nil", + impExt: nil, + expectedExt: nil, + }, + { + name: "empty ext is cleared", + impExt: json.RawMessage(``), + expectedExt: nil, + }, + { + name: "ext holding only bidder is cleared entirely", + impExt: json.RawMessage(`{"bidder":{"publisherId":"pub-1"}}`), + expectedExt: nil, + }, + { + name: "non-bidder keys are preserved", + impExt: json.RawMessage(`{"bidder":{"publisherId":"pub-1"},"gpid":"/1234/home"}`), + expectedExt: json.RawMessage(`{"gpid":"/1234/home"}`), + }, + { + name: "malformed json returns an error", + impExt: json.RawMessage(`{"bidder":`), + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + imp := openrtb2.Imp{ID: "imp-1", Ext: tc.impExt} + err := stripBidderExt(&imp) + + if tc.expectError { + assert.Error(t, err) + return + } + + assert.NoError(t, err) + if tc.expectedExt == nil { + assert.Nil(t, imp.Ext) + } else { + assert.JSONEq(t, string(tc.expectedExt), string(imp.Ext)) + } + }) + } +} + +func TestSetPublisherIDEmpty(t *testing.T) { + // An empty publisher ID must leave the request untouched rather than creating an + // empty site/app publisher object. + request := openrtb2.BidRequest{ID: "req-1"} + setPublisherID(&request, "") + + assert.Nil(t, request.Site) + assert.Nil(t, request.App) +} diff --git a/adapters/peak226/peak226test/exemplary/banner.json b/adapters/peak226/peak226test/exemplary/banner.json new file mode 100644 index 00000000000..e2311343bca --- /dev/null +++ b/adapters/peak226/peak226test/exemplary/banner.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "existing-pub-id" + } + }, + "imp": [ + { + "id": "imp-1", + "bidfloor": 1.5, + "bidfloorcur": "USD", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "bidfloor": 1.5, + "bidfloorcur": "USD", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/exemplary/multiformat.json b/adapters/peak226/peak226test/exemplary/multiformat.json new file mode 100644 index 00000000000..d798f201b7c --- /dev/null +++ b/adapters/peak226/peak226test/exemplary/multiformat.json @@ -0,0 +1,139 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + }, + "native": { + "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + }, + "native": { + "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}", + "ver": "1.2" + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-banner", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + }, + { + "id": "bid-video", + "impid": "imp-1", + "price": 4.75, + "adm": "", + "crid": "cr-2", + "adomain": ["advertiser.com"], + "w": 640, + "h": 480, + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-banner", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-video", + "impid": "imp-1", + "price": 4.75, + "adm": "", + "crid": "cr-2", + "adomain": ["advertiser.com"], + "w": 640, + "h": 480, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/exemplary/native.json b/adapters/peak226/peak226test/exemplary/native.json new file mode 100644 index 00000000000..29b44b7c8a2 --- /dev/null +++ b/adapters/peak226/peak226test/exemplary/native.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "existing-pub-id" + } + }, + "imp": [ + { + "id": "imp-1", + "native": { + "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "native": { + "request": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}", + "ver": "1.2" + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 1.25, + "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"Hello\"}}]}", + "crid": "cr-3", + "adomain": ["advertiser.com"], + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 1.25, + "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"Hello\"}}]}", + "crid": "cr-3", + "adomain": ["advertiser.com"], + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/exemplary/video.json b/adapters/peak226/peak226test/exemplary/video.json new file mode 100644 index 00000000000..75c2e8020c6 --- /dev/null +++ b/adapters/peak226/peak226test/exemplary/video.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test", + "region": "eu" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://eu.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 4.75, + "adm": "", + "crid": "cr-2", + "adomain": ["advertiser.com"], + "w": 640, + "h": 480, + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 4.75, + "adm": "", + "crid": "cr-2", + "adomain": ["advertiser.com"], + "w": 640, + "h": 480, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json b/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json new file mode 100644 index 00000000000..c890cbd80d0 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/app-device-eids-passthrough.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app" + }, + "device": { + "ifa": "AEBE52E7-03EE-455A-B3C4-E57283966239", + "os": "iOS" + }, + "user": { + "eids": [ + { + "source": "uidapi.com", + "uids": [{"id": "UID2-TOKEN", "atype": 3}] + } + ] + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app", + "publisher": { + "id": "pub-test" + } + }, + "device": { + "ifa": "AEBE52E7-03EE-455A-B3C4-E57283966239", + "os": "iOS" + }, + "user": { + "eids": [ + { + "source": "uidapi.com", + "uids": [{"id": "UID2-TOKEN", "atype": 3}] + } + ] + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 320, "h": 50}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json b/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json new file mode 100644 index 00000000000..bc24f38461c --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/app-zero-ifa-cleared.json @@ -0,0 +1,99 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app" + }, + "device": { + "ifa": "00000000-0000-0000-0000-000000000000", + "os": "iOS" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app", + "publisher": { + "id": "pub-test" + } + }, + "device": { + "os": "iOS" + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 320, "h": 50}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/auction-price-macro.json b/adapters/peak226/peak226test/supplemental/auction-price-macro.json new file mode 100644 index 00000000000..a23da8cb57d --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/auction-price-macro.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 3.25, + "adm": "
ad
", + "nurl": "https://count.viddea.com/nurl?p=${AUCTION_PRICE}", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 3.25, + "adm": "
ad
", + "nurl": "https://count.viddea.com/nurl?p=3.25", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/bad-server-response.json b/adapters/peak226/peak226test/supplemental/bad-server-response.json new file mode 100644 index 00000000000..04b4d95330a --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/bad-server-response.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": "invalid-response-body" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "bad server response: ", + "comparison": "startswith" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json new file mode 100644 index 00000000000..579b676c40f --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion-error.json @@ -0,0 +1,44 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "GBP": 0.85 + } + }, + "usepbsrates": false + } + } + }, + "imp": [ + { + "id": "imp-1", + "bidfloor": 10, + "bidfloorcur": "EUR", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "Currency conversion rate not found: 'EUR' => 'USD'", + "comparison": "literal" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json new file mode 100644 index 00000000000..7ece3789507 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/bidfloor-currency-conversion.json @@ -0,0 +1,118 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.1 + } + } + } + } + }, + "imp": [ + { + "id": "imp-1", + "bidfloor": 10, + "bidfloorcur": "EUR", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.1 + } + } + } + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "bidfloor": 11, + "bidfloorcur": "USD", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 12.0, + "adm": "
ad
", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 12.0, + "adm": "
ad
", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/empty-seatbid.json b/adapters/peak226/peak226test/supplemental/empty-seatbid.json new file mode 100644 index 00000000000..4f720e48d9d --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/empty-seatbid.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json b/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json new file mode 100644 index 00000000000..fdff6b7c3b3 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/imp-ext-passthrough.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + }, + "gpid": "/1234/home#div-banner", + "tid": "tx-1", + "data": { + "pbadslot": "/1234/home#div-banner" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "gpid": "/1234/home#div-banner", + "tid": "tx-1", + "data": { + "pbadslot": "/1234/home#div-banner" + } + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "adomain": ["advertiser.com"], + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json b/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json new file mode 100644 index 00000000000..edf197065be --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/invalid-bidder-ext.json @@ -0,0 +1,26 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": "not-an-object" + } + } + ] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [ + { + "value": "imp #imp-1: ", + "comparison": "startswith" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json b/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json new file mode 100644 index 00000000000..2dfad1d9b89 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/invalid-imp-ext.json @@ -0,0 +1,24 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": "not-an-object" + } + ] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [ + { + "value": "imp #imp-1: ", + "comparison": "startswith" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json b/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json new file mode 100644 index 00000000000..1627cb8b7a3 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/multi-publisher-same-region-split.json @@ -0,0 +1,160 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-pub-a", + "bidfloor": 1.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 300, "h": 250}] }, + "ext": { + "bidder": { "publisherId": "pub-a", "placementId": "plc-a" } + } + }, + { + "id": "imp-pub-b", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 320, "h": 50}] }, + "ext": { + "bidder": { "publisherId": "pub-b", "placementId": "plc-b" } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { "id": "pub-a" } + }, + "imp": [ + { + "id": "imp-pub-a", + "tagid": "plc-a", + "bidfloor": 1.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 300, "h": 250}] } + } + ] + }, + "impIDs": ["imp-pub-a"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-pub-a", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-pub-a-1", + "impid": "imp-pub-a", + "price": 1.5, + "adm": "
pub-a-ad
", + "crid": "cr-pub-a-1", + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + }, + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { "id": "pub-b" } + }, + "imp": [ + { + "id": "imp-pub-b", + "tagid": "plc-b", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 320, "h": 50}] } + } + ] + }, + "impIDs": ["imp-pub-b"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-pub-b", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-pub-b-1", + "impid": "imp-pub-b", + "price": 2.5, + "adm": "
pub-b-ad
", + "crid": "cr-pub-b-1", + "w": 320, + "h": 50, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-pub-a-1", + "impid": "imp-pub-a", + "price": 1.5, + "adm": "
pub-a-ad
", + "crid": "cr-pub-a-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-pub-b-1", + "impid": "imp-pub-b", + "price": 2.5, + "adm": "
pub-b-ad
", + "crid": "cr-pub-b-1", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/multi-region-split.json b/adapters/peak226/peak226test/supplemental/multi-region-split.json new file mode 100644 index 00000000000..4ce83bc4109 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/multi-region-split.json @@ -0,0 +1,160 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-us", + "bidfloor": 1.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 300, "h": 250}] }, + "ext": { + "bidder": { "publisherId": "pub-us-1", "placementId": "plc-us-1" } + } + }, + { + "id": "imp-jp", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 320, "h": 50}] }, + "ext": { + "bidder": { "publisherId": "pub-jp-1", "placementId": "plc-jp-1", "region": "jp" } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { "id": "pub-us-1" } + }, + "imp": [ + { + "id": "imp-us", + "tagid": "plc-us-1", + "bidfloor": 1.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 300, "h": 250}] } + } + ] + }, + "impIDs": ["imp-us"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-us", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-us-1", + "impid": "imp-us", + "price": 1.5, + "adm": "
us-ad
", + "crid": "cr-us-1", + "w": 300, + "h": 250, + "mtype": 1 + } + ] + } + ] + } + } + }, + { + "expectedRequest": { + "uri": "https://jp.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { "id": "pub-jp-1" } + }, + "imp": [ + { + "id": "imp-jp", + "tagid": "plc-jp-1", + "bidfloor": 2.0, + "bidfloorcur": "USD", + "banner": { "format": [{"w": 320, "h": 50}] } + } + ] + }, + "impIDs": ["imp-jp"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-jp", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-jp-1", + "impid": "imp-jp", + "price": 2.5, + "adm": "
jp-ad
", + "crid": "cr-jp-1", + "w": 320, + "h": 50, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-us-1", + "impid": "imp-us", + "price": 1.5, + "adm": "
us-ad
", + "crid": "cr-us-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-jp-1", + "impid": "imp-jp", + "price": 2.5, + "adm": "
jp-ad
", + "crid": "cr-jp-1", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json b/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json new file mode 100644 index 00000000000..2aa46e3afb0 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/region-jp-app-publisher.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test", + "region": "jp" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://jp.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.example.app", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 320, "h": 50}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 0.85, + "adm": "
ad
", + "crid": "cr-1", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/status-code-bad-request.json b/adapters/peak226/peak226test/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..ff0e5b639e8 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/status-code-bad-request.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/status-code-no-content.json b/adapters/peak226/peak226test/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..20f2df88d55 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/status-code-no-content.json @@ -0,0 +1,53 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/peak226/peak226test/supplemental/status-code-server-error.json b/adapters/peak226/peak226test/supplemental/status-code-server-error.json new file mode 100644 index 00000000000..ab2df6c0c4a --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/status-code-server-error.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json b/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json new file mode 100644 index 00000000000..365ba4f7070 --- /dev/null +++ b/adapters/peak226/peak226test/supplemental/unrecognized-mtype.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "publisherId": "pub-test", + "placementId": "plc-test" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us.a.viddea.com/edge_direct", + "body": { + "id": "test-request-id", + "site": { + "page": "https://publisher.example.com/page", + "publisher": { + "id": "pub-test" + } + }, + "imp": [ + { + "id": "imp-1", + "tagid": "plc-test", + "banner": { + "format": [{"w": 300, "h": 250}] + } + } + ] + }, + "impIDs": ["imp-1"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-1", + "cur": "USD", + "seatbid": [ + { + "seat": "peak226", + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + { + "id": "bid-2", + "impid": "imp-2", + "price": 1.0, + "adm": "audio-not-supported", + "crid": "cr-2", + "mtype": 3 + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unrecognized bid type for impression imp-2", + "comparison": "literal" + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 2.5, + "adm": "
ad
", + "crid": "cr-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index c8da847f761..f889228445f 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -184,6 +184,7 @@ import ( "github.com/prebid/prebid-server/v4/adapters/outbrain" "github.com/prebid/prebid-server/v4/adapters/ownadx" "github.com/prebid/prebid-server/v4/adapters/pangle" + "github.com/prebid/prebid-server/v4/adapters/peak226" "github.com/prebid/prebid-server/v4/adapters/pgamssp" "github.com/prebid/prebid-server/v4/adapters/pixfuture" "github.com/prebid/prebid-server/v4/adapters/playdigo" @@ -465,6 +466,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderOutbrain: outbrain.Builder, openrtb_ext.BidderOwnAdx: ownadx.Builder, openrtb_ext.BidderPangle: pangle.Builder, + openrtb_ext.BidderPeak226: peak226.Builder, openrtb_ext.BidderPGAMSsp: pgamssp.Builder, openrtb_ext.BidderPixfuture: pixfuture.Builder, openrtb_ext.BidderPlaydigo: playdigo.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 4858f69ef8f..d23f5f70326 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -202,6 +202,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderOutbrain, BidderOwnAdx, BidderPangle, + BidderPeak226, BidderPGAMSsp, BidderPixfuture, BidderPlaydigo, @@ -587,6 +588,7 @@ const ( BidderOutbrain BidderName = "outbrain" BidderOwnAdx BidderName = "ownadx" BidderPangle BidderName = "pangle" + BidderPeak226 BidderName = "peak226" BidderPGAMSsp BidderName = "pgamssp" BidderPixfuture BidderName = "pixfuture" BidderPlaydigo BidderName = "playdigo" diff --git a/openrtb_ext/imp_peak226.go b/openrtb_ext/imp_peak226.go new file mode 100644 index 00000000000..74f01412166 --- /dev/null +++ b/openrtb_ext/imp_peak226.go @@ -0,0 +1,8 @@ +package openrtb_ext + +// ImpExtPeak226 defines the contract for bidrequest.imp[i].ext.prebid.bidder.peak226 +type ImpExtPeak226 struct { + PublisherID string `json:"publisherId"` + PlacementID string `json:"placementId"` + Region string `json:"region,omitempty"` +} diff --git a/static/bidder-info/peak226.yaml b/static/bidder-info/peak226.yaml new file mode 100644 index 00000000000..c5c0ecc8b3e --- /dev/null +++ b/static/bidder-info/peak226.yaml @@ -0,0 +1,22 @@ +endpoint: "https://{{.Region}}.a.viddea.com/edge_direct" +geoscope: + - global +maintainer: + email: "support@edge226.com" +gvlVendorID: 1202 +modifyingVastXmlAllowed: false +openrtb: + version: 2.6 + gpp-supported: true + multiformat-supported: true +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/peak226.json b/static/bidder-params/peak226.json new file mode 100644 index 00000000000..f08d0cfd5e4 --- /dev/null +++ b/static/bidder-params/peak226.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Peak226 Adapter Params", + "description": "A schema which validates params accepted by the Peak226 adapter", + "type": "object", + "properties": { + "publisherId": { + "type": "string", + "description": "Publisher/account ID", + "minLength": 1 + }, + "placementId": { + "type": "string", + "description": "Placement ID for this ad unit", + "minLength": 1 + }, + "region": { + "type": "string", + "description": "Data center to send the request to. Defaults to us.", + "enum": ["us", "eu", "jp"] + } + }, + "required": ["publisherId", "placementId"] +}