diff --git a/adapters/beop/beop.go b/adapters/beop/beop.go new file mode 100644 index 00000000000..ffbb4c94bd8 --- /dev/null +++ b/adapters/beop/beop.go @@ -0,0 +1,172 @@ +package beop + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +type adapter struct { + endpoint string +} + +func Builder( + bidderName openrtb_ext.BidderName, + config config.Adapter, + server config.Server) ( + adapters.Bidder, error, +) { + bidder := &adapter{ + endpoint: config.Endpoint, + } + return bidder, nil +} + +func (a *adapter) getRequestExtImpBeop(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBeop, error) { + var bidderExt adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + var beopExt openrtb_ext.ExtImpBeop + if err := jsonutil.Unmarshal(bidderExt.Bidder, &beopExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + return &beopExt, nil +} + +func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpBeop) (string, error) { + url, _ := url.Parse(a.endpoint) + query := url.Query() + if pid := params.BeopPublisherID; len(pid) != 0 { + query.Set("pid", pid) + } + if nid := params.BeopNetworkID; len(nid) != 0 { + query.Set("nid", nid) + } + if nptnid := params.BeopNetworkPartnerID; len(nptnid) != 0 { + query.Set("nptnid", nptnid) + } + url.RawQuery = query.Encode() + return url.String(), nil +} + +func (a *adapter) MakeRequests( + request *openrtb2.BidRequest, + requestInfo *adapters.ExtraRequestInfo) ( + []*adapters.RequestData, []error, +) { + var beopExt *openrtb_ext.ExtImpBeop + var err error + + beopExt, err = a.getRequestExtImpBeop(&request.Imp[0]) + if err != nil { + return nil, []error{err} + } + + requestJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + url, err := a.buildEndpointURL(beopExt) + if err != nil { + return nil, []error{err} + } + + requestData := &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: requestJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + } + + return []*adapters.RequestData{requestData}, nil +} + +func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { + mType := bid.MType + var bidType openrtb_ext.BidType + switch mType { + case openrtb2.MarkupBanner: + bidType = openrtb_ext.BidTypeBanner + case openrtb2.MarkupVideo: + bidType = openrtb_ext.BidTypeVideo + default: + return bidType, fmt.Errorf("Failed to parse bid mType for impression \"%s\"", bid.ImpID) + } + return bidType, nil +} + +func (a *adapter) MakeBids( + request *openrtb2.BidRequest, + requestData *adapters.RequestData, + responseData *adapters.ResponseData) ( + *adapters.BidderResponse, []error, +) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode == http.StatusServiceUnavailable { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Service Unavailable. Status Code: [ %d ] ", responseData.StatusCode), + }} + } + + if responseData.StatusCode == http.StatusBadRequest { + err := &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), + } + return nil, []error{err} + } + + var responseBody openrtb2.BidResponse + if err := jsonutil.Unmarshal(responseData.Body, &responseBody); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Bad Server Response", + }} + } + + if len(responseBody.SeatBid) == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Empty SeatBid array", + }} + } + + bidResponseFinal := adapters.NewBidderResponseWithBidsCapacity(len(responseBody.SeatBid[0].Bid)) + seatBid := responseBody.SeatBid[0] + var errors []error + for i := range seatBid.Bid { + bid := &seatBid.Bid[i] + bidType, err := getMediaTypeForBid(bid) + if err != nil { + errors = append(errors, err) + continue + } + bidResponseFinal.Bids = append(bidResponseFinal.Bids, &adapters.TypedBid{ + Bid: bid, + BidType: bidType, + }) + } + if len(bidResponseFinal.Bids) == 0 { + return nil, errors + } + return bidResponseFinal, errors +} diff --git a/adapters/beop/beop_test.go b/adapters/beop/beop_test.go new file mode 100644 index 00000000000..5960bc09f1c --- /dev/null +++ b/adapters/beop/beop_test.go @@ -0,0 +1,21 @@ +package beop + +import ( + "testing" + + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderBeop, config.Adapter{ + Endpoint: "http://hb-test.collectiveaudience.co/rtb/bid"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "beoptest", bidder) +} diff --git a/adapters/beop/beoptest/exemplary/banner-web-with-nid.json b/adapters/beop/beoptest/exemplary/banner-web-with-nid.json new file mode 100644 index 00000000000..e0c6f9a880c --- /dev/null +++ b/adapters/beop/beoptest/exemplary/banner-web-with-nid.json @@ -0,0 +1,155 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "nid": "aaaaaaaaaaaaaaaaaaaaaaaa", + "nptnid": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?nid=aaaaaaaaaaaaaaaaaaaaaaaa&nptnid=1234", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "nid": "aaaaaaaaaaaaaaaaaaaaaaaa", + "nptnid": "1234" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 320, + "h": 50, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 320, + "h": 50, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/beop/beoptest/exemplary/banner-web.json b/adapters/beop/beoptest/exemplary/banner-web.json new file mode 100644 index 00000000000..a757171e346 --- /dev/null +++ b/adapters/beop/beoptest/exemplary/banner-web.json @@ -0,0 +1,153 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 320, + "h": 50, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 320, + "h": 50, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/beop/beoptest/exemplary/video-web.json b/adapters/beop/beoptest/exemplary/video-web.json new file mode 100644 index 00000000000..bd4ff7acfec --- /dev/null +++ b/adapters/beop/beoptest/exemplary/video-web.json @@ -0,0 +1,163 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 30, + "maxduration": 150 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 30, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 1280, + "h": 720, + "mtype": 2, + "ext": { + "prebid": { + "type": "video" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 1280, + "h": 720, + "mtype": 2, + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/empty-seatbid-array.json b/adapters/beop/beoptest/supplemental/empty-seatbid-array.json new file mode 100644 index 00000000000..0b709b2b935 --- /dev/null +++ b/adapters/beop/beoptest/supplemental/empty-seatbid-array.json @@ -0,0 +1,111 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-id", + "seatbid": [] + } + } + } + ], + "mockResponse": { + "status": 200, + "body": "invalid response" + }, + "expectedMakeBidsErrors": [ + { + "value": "Empty SeatBid array", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/invalid-bid-type.json b/adapters/beop/beoptest/supplemental/invalid-bid-type.json new file mode 100644 index 00000000000..d0a8580963d --- /dev/null +++ b/adapters/beop/beoptest/supplemental/invalid-bid-type.json @@ -0,0 +1,131 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "123456789", + "impid": "impression-id", + "price": 2, + "adm": "adm code", + "adomain": [ + "testdomain.com" + ], + "crid": "100", + "w": 320, + "h": 50, + "mtype": 3, + "ext": { + "prebid": { + "type": "audio" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Failed to parse bid mType for impression \"impression-id\"", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/invalid-response.json b/adapters/beop/beoptest/supplemental/invalid-response.json new file mode 100644 index 00000000000..8c5537acefd --- /dev/null +++ b/adapters/beop/beoptest/supplemental/invalid-response.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Bad Server Response", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/missing-bidder-params.json b/adapters/beop/beoptest/supplemental/missing-bidder-params.json new file mode 100644 index 00000000000..4c2de07072d --- /dev/null +++ b/adapters/beop/beoptest/supplemental/missing-bidder-params.json @@ -0,0 +1,41 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": {} + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "ext.bidder not provided", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/status-code-bad-request.json b/adapters/beop/beoptest/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..f8c0eb2d564 --- /dev/null +++ b/adapters/beop/beoptest/supplemental/status-code-bad-request.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/beoptest/supplemental/status-code-no-content.json b/adapters/beop/beoptest/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..25280c2395d --- /dev/null +++ b/adapters/beop/beoptest/supplemental/status-code-no-content.json @@ -0,0 +1,98 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/beop/beoptest/supplemental/status-code-service-unavailable.json b/adapters/beop/beoptest/supplemental/status-code-service-unavailable.json new file mode 100644 index 00000000000..ce3742ab852 --- /dev/null +++ b/adapters/beop/beoptest/supplemental/status-code-service-unavailable.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "request-id", + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "tmax": 1000, + "user": { + "id": "some-user" + }, + "imp": [ + { + "id": "impression-id", + "tagid": "tid", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "uri": "http://hb-test.collectiveaudience.co/rtb/bid?pid=aaaaaaaaaaaaaaaaaaaaaaaa", + "body": { + "id": "request-id", + "device": { + "ua": "useragent", + "ip": "100.100.100.100", + "language": "en" + }, + "imp": [ + { + "id": "impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "tid", + "ext": { + "bidder": { + "pid": "aaaaaaaaaaaaaaaaaaaaaaaa" + } + } + } + ], + "site": { + "page": "test.com/page", + "domain": "test.com", + "cat": [ + "IAB9-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "id": "some-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "impression-id" + ] + }, + "mockResponse": { + "status": 503 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Service Unavailable. Status Code: [ 503 ] ", + "comparison": "literal" + } + ] +} diff --git a/adapters/beop/params_test.go b/adapters/beop/params_test.go new file mode 100644 index 00000000000..5b854bd3af5 --- /dev/null +++ b/adapters/beop/params_test.go @@ -0,0 +1,49 @@ +package beop + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/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.BidderBeop, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adverxo 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.BidderBeop, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{ "pid": "aaaaaaaaaaaaaaaaaaaaaaaa"}`, + `{ "nid": "aaaaaaaaaaaaaaaaaaaaaaaa", "ntpnid": "1234"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `{}`, + `{ "fid": 5 }`, + `{ "pid": 5 }`, + `{ "pid": 5 }`, + `{ "nid": "", "nptnid": "" }`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 9a2aaa0bd21..ac33c868db8 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -52,6 +52,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/beachfront" "github.com/prebid/prebid-server/v3/adapters/beintoo" "github.com/prebid/prebid-server/v3/adapters/bematterfull" + "github.com/prebid/prebid-server/v3/adapters/beop" "github.com/prebid/prebid-server/v3/adapters/between" "github.com/prebid/prebid-server/v3/adapters/beyondmedia" "github.com/prebid/prebid-server/v3/adapters/bidmachine" @@ -318,6 +319,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderBeachfront: beachfront.Builder, openrtb_ext.BidderBeintoo: beintoo.Builder, openrtb_ext.BidderBematterfull: bematterfull.Builder, + openrtb_ext.BidderBeop: beop.Builder, openrtb_ext.BidderBetween: between.Builder, openrtb_ext.BidderBeyondMedia: beyondmedia.Builder, openrtb_ext.BidderBidmachine: bidmachine.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 8cd543c1f16..9eed12b7650 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -68,6 +68,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderBeachfront, BidderBeintoo, BidderBematterfull, + BidderBeop, BidderBetween, BidderBeyondMedia, BidderBidmachine, @@ -441,6 +442,7 @@ const ( BidderBeachfront BidderName = "beachfront" BidderBeintoo BidderName = "beintoo" BidderBematterfull BidderName = "bematterfull" + BidderBeop BidderName = "beop" BidderBetween BidderName = "between" BidderBeyondMedia BidderName = "beyondmedia" BidderBidmachine BidderName = "bidmachine" diff --git a/openrtb_ext/imp_beop.go b/openrtb_ext/imp_beop.go new file mode 100644 index 00000000000..797ac92ef02 --- /dev/null +++ b/openrtb_ext/imp_beop.go @@ -0,0 +1,7 @@ +package openrtb_ext + +type ExtImpBeop struct { + BeopPublisherID string `json:"pid,omitempty"` + BeopNetworkID string `json:"nid,omitempty"` + BeopNetworkPartnerID string `json:"nptnid,omitempty"` +} diff --git a/static/bidder-info/beop.yaml b/static/bidder-info/beop.yaml new file mode 100644 index 00000000000..1129818f857 --- /dev/null +++ b/static/bidder-info/beop.yaml @@ -0,0 +1,12 @@ +endpoint: "https://hb.collectiveaudience.co/rtb/bid" +endpointCompression: gzip +maintainer: + email: tech@collectiveaudience.co +gvlVendorID: 666 +openrtb: + version: 2.6 +capabilities: + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/beop.json b/static/bidder-params/beop.json new file mode 100644 index 00000000000..8b34ac91f70 --- /dev/null +++ b/static/bidder-params/beop.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "BeOp Adapter Params", + "description": "A schema which validates params accepted by the BeOp adapter", + "type": "object", + "properties": { + "pid": { + "type": "string", + "description": "Beop publisher ID" + }, + "nid": { + "type": "string", + "description": "Beop Network ID" + }, + "ntpnid": { + "type": "string", + "description": "Network partner ID" + } + }, + "oneOf": [ + { + "required": [ + "pid" + ] + }, + { + "required": [ + "nid", + "ntpnid" + ] + } + ] +}