diff --git a/adapters/engagebdr/engagebdr.go b/adapters/engagebdr/engagebdr.go new file mode 100644 index 00000000000..48b103ae7b4 --- /dev/null +++ b/adapters/engagebdr/engagebdr.go @@ -0,0 +1,150 @@ +package engagebdr + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + + "fmt" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" +) + +type EngageBDRAdapter struct { + http *adapters.HTTPAdapter + URI string +} + +func (adapter *EngageBDRAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + errors := make([]error, 0, len(request.Imp)) + + if request.Imp == nil || len(request.Imp) == 0 { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Invalid BidRequest. No valid imp."), + }) + return nil, errors + } + + // EngageBDR uses different sspid parameters for banner and video. + sspidImps := make(map[string][]openrtb.Imp) + for _, imp := range request.Imp { + + if imp.Audio != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Ignoring imp id=%s, invalid MediaType. EngageBDR only supports Banner, Video and Native.", imp.ID), + }) + continue + } + + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding extImpBidder, err: %s.", imp.ID, err), + }) + continue + } + impExt := openrtb_ext.ExtImpEngageBDR{} + err := json.Unmarshal(bidderExt.Bidder, &impExt) + if err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding impExt, err: %s.", imp.ID, err), + }) + continue + } + if impExt.Sspid == "" { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Ignoring imp id=%s, no sspid present.", imp.ID), + }) + continue + } + sspidImps[impExt.Sspid] = append(sspidImps[impExt.Sspid], imp) + } + + var adapterRequests []*adapters.RequestData + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + + for sspid, imps := range sspidImps { + if len(imps) > 0 { + // Make a copy as we don't want to change the original request + reqCopy := *request + reqCopy.Imp = imps + reqJSON, err := json.Marshal(reqCopy) + if err != nil { + errors = append(errors, err) + return nil, errors + } + adapterReq := adapters.RequestData{ + Method: "POST", + Uri: adapter.URI + "?zoneid=" + sspid, + Body: reqJSON, + Headers: headers, + } + adapterRequests = append(adapterRequests, &adapterReq) + } + } + + return adapterRequests, errors +} + +func (adapter *EngageBDRAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), + }) + } + } + return bidResponse, nil +} + +func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType { + mediaType := openrtb_ext.BidTypeBanner + for _, imp := range imps { + if imp.ID == impId { + if imp.Video != nil { + mediaType = openrtb_ext.BidTypeVideo + } else if imp.Native != nil { + mediaType = openrtb_ext.BidTypeNative + } + return mediaType + } + } + return mediaType +} + +func NewEngageBDRBidder(client *http.Client, endpoint string) *EngageBDRAdapter { + adapter := &adapters.HTTPAdapter{Client: client} + return &EngageBDRAdapter{ + http: adapter, + URI: endpoint, + } +} diff --git a/adapters/engagebdr/engagebdr_test.go b/adapters/engagebdr/engagebdr_test.go new file mode 100644 index 00000000000..069cf8b8210 --- /dev/null +++ b/adapters/engagebdr/engagebdr_test.go @@ -0,0 +1,11 @@ +package engagebdr + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "net/http" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "engagebdrtest", NewEngageBDRBidder(new(http.Client), "http://dsp.bnmla.com/hb")) +} diff --git a/adapters/engagebdr/engagebdrtest/exemplary/banner.json b/adapters/engagebdr/engagebdrtest/exemplary/banner.json new file mode 100644 index 00000000000..92b79e8f349 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/exemplary/banner.json @@ -0,0 +1,97 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99999" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99999", + "body":{ + "id": "test-request-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99999" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id" : "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": ["advertiserdomain.com"], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/exemplary/multi-banner.json b/adapters/engagebdr/engagebdrtest/exemplary/multi-banner.json new file mode 100644 index 00000000000..d11e38c46fc --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/exemplary/multi-banner.json @@ -0,0 +1,154 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99999" + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "sspid": "99999" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99999", + "body":{ + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99999" + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "sspid":"99999" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id" : "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + { + "id" : "test-imp-id-2", + "impid": "test-imp-id-2", + "price": 7.50, + "adid": "abcde-12345-2", + "adm": "
", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345-2", + "w": 320, + "h": 50 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": ["advertiserdomain.com"], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "id": "test-imp-id-2", + "impid": "test-imp-id-2", + "price": 7.50, + "adid": "abcde-12345-2", + "adm": "
", + "adomain": ["advertiserdomain.com"], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345-2", + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/exemplary/multi-video.json b/adapters/engagebdr/engagebdrtest/exemplary/multi-video.json new file mode 100644 index 00000000000..9506c963578 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/exemplary/multi-video.json @@ -0,0 +1,159 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "mimes": null, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99998" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "w": 320, + "mimes": null, + "h": 50 + }, + "ext": { + "bidder": { + "sspid": "99998" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99998", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "mimes": null, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99998" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "w": 320, + "mimes": null, + "h": 50 + }, + "ext": { + "bidder": { + "sspid": "99998" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + { + "id": "test-imp-id-2", + "impid": "test-imp-id-2", + "price": 7.81, + "adid": "abcde-12345-2", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345-2", + "w": 320, + "h": 50 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "video" + }, + { + "bid": { + "id": "test-imp-id-2", + "impid": "test-imp-id-2", + "price": 7.81, + "adid": "abcde-12345-2", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345-2", + "w": 320, + "h": 50 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/engagebdr/engagebdrtest/exemplary/native.json b/adapters/engagebdr/engagebdrtest/exemplary/native.json new file mode 100644 index 00000000000..963194fb8bd --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/exemplary/native.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}", + "ver":"1.1" + }, + "ext": { + "bidder": { + "sspid": "99997" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99997", + "body":{ + "id": "test-request-id", + "imp": [{ + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}},{\"id\":6,\"required\":0,\"data\":{\"type\":500}}]}", + "ver":"1.1" + }, + "ext": { + "bidder": { + "sspid":"99997" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id" : "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "{\"native\":{\"link\":{\"url\":\"https://rtb-use.mfadsrvr.com/click/ESoNneAwqCPnn97YSh0EoJzPUnSmqwdERCYPCdrHr1_TJz_V-x2xjMgxcROeooIH5fe1exAsWt2aqg1ESQEVQM8i0TpI1QBcV4V87Uzmf_XfAR6-6xqvqfGuDs-pJDWqAYz0P0OtHlrvVztlMdWu6JT9_GAtVAnB9gp0JchRJLSqr1h_GRZwuNUri7NvveTD7m8ZUHKNFldKPwHCbom120NFFn2Z3a6v0owsZfIgOff-1YyvZ9WkzVr3755kGRT_D1FUy3r2kurY8HdfeTiRuZAajluniEkJql7yGlS6hVfQ3vT3X93BKIo1F_A3o4bfywT49tM-3l2X8vwlc-w9X-B5VudQPJ8kboJZ2OuaD5AN///\"},\"assets\":[{\"id\":0,\"title\":{\"text\":\"4 Signs Your Heart Is Quietly Failing You\"},\"required\":1},{\"id\":3,\"img\":{\"w\":1200,\"h\":627,\"url\":\"https://de9a11s35xj3d.cloudfront.net/5922785fd53de8084607850abdaace4f.jpg\"}},{\"id\":4,\"data\":{\"value\":\"PhysioTru\"}},{\"id\":6,\"data\":{\"value\":\"\\n How To Avoid A Heart Attack (Do This For 7 Seconds Twice A Day)\\n \"}}],\"imptrackers\":[\"https://rtb-use.mfadsrvr.com/imp_c2s/v1/ESoNneAwqCPnn97YSh0EoJzPUnSmqwdERCYPCdrHr1_TJz_V-x2xjMgxcROeooIH5fe1exAsWt2aqg1ESQEVQM8i0TpI1QBcV4V87Uzmf_XfAR6-6xqvqfGuDs-pJDWqAYz0P0OtHlrvVztlMdWu6JT9_GAtVAnB9gp0JchRJLSqr1h_GRZwuNUri7NvveTD7m8ZUHKNFldKPwHCbom120NFFn2Z3a6v0owsZfIgOff-1YyvZ9WkzVr3755kGRT_D1FUy3r2kurY8HdfeTiRuZAajluniEkJql7yGlS6hVfQ3vT3X93BKIo1F_A3o4bfywT49tM-3l2X8vwlc-w9X-B5VudQPJ8kboJZ2OuaD5AN/${AUCTION_PRICE}\"],\"ver\":1}}", + "adomain": [ + "advertiserdomain.com" + ], + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "{\"native\":{\"link\":{\"url\":\"https://rtb-use.mfadsrvr.com/click/ESoNneAwqCPnn97YSh0EoJzPUnSmqwdERCYPCdrHr1_TJz_V-x2xjMgxcROeooIH5fe1exAsWt2aqg1ESQEVQM8i0TpI1QBcV4V87Uzmf_XfAR6-6xqvqfGuDs-pJDWqAYz0P0OtHlrvVztlMdWu6JT9_GAtVAnB9gp0JchRJLSqr1h_GRZwuNUri7NvveTD7m8ZUHKNFldKPwHCbom120NFFn2Z3a6v0owsZfIgOff-1YyvZ9WkzVr3755kGRT_D1FUy3r2kurY8HdfeTiRuZAajluniEkJql7yGlS6hVfQ3vT3X93BKIo1F_A3o4bfywT49tM-3l2X8vwlc-w9X-B5VudQPJ8kboJZ2OuaD5AN///\"},\"assets\":[{\"id\":0,\"title\":{\"text\":\"4 Signs Your Heart Is Quietly Failing You\"},\"required\":1},{\"id\":3,\"img\":{\"w\":1200,\"h\":627,\"url\":\"https://de9a11s35xj3d.cloudfront.net/5922785fd53de8084607850abdaace4f.jpg\"}},{\"id\":4,\"data\":{\"value\":\"PhysioTru\"}},{\"id\":6,\"data\":{\"value\":\"\\n How To Avoid A Heart Attack (Do This For 7 Seconds Twice A Day)\\n \"}}],\"imptrackers\":[\"https://rtb-use.mfadsrvr.com/imp_c2s/v1/ESoNneAwqCPnn97YSh0EoJzPUnSmqwdERCYPCdrHr1_TJz_V-x2xjMgxcROeooIH5fe1exAsWt2aqg1ESQEVQM8i0TpI1QBcV4V87Uzmf_XfAR6-6xqvqfGuDs-pJDWqAYz0P0OtHlrvVztlMdWu6JT9_GAtVAnB9gp0JchRJLSqr1h_GRZwuNUri7NvveTD7m8ZUHKNFldKPwHCbom120NFFn2Z3a6v0owsZfIgOff-1YyvZ9WkzVr3755kGRT_D1FUy3r2kurY8HdfeTiRuZAajluniEkJql7yGlS6hVfQ3vT3X93BKIo1F_A3o4bfywT49tM-3l2X8vwlc-w9X-B5VudQPJ8kboJZ2OuaD5AN/${AUCTION_PRICE}\"],\"ver\":1}}", + "adomain": ["advertiserdomain.com"], + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "native" + } + ] + } + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/exemplary/video.json b/adapters/engagebdr/engagebdrtest/exemplary/video.json new file mode 100644 index 00000000000..53c00dc4523 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/exemplary/video.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "mimes": null, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99998" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99998", + "body":{ + "id": "test-request-id", + "imp": [{ + "id": "test-imp-id", + "video": { + "w": 300, + "mimes": null, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99998" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain":[ + "advertiserdomain.com" + ], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "\nStatic VASTStatic VAST Tag", + "adomain": ["advertiserdomain.com"], + "iurl": "https://cdn0.bnmla.com/vtest.xml", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "video" + } + ] + + } + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/params/race/banner.json b/adapters/engagebdr/engagebdrtest/params/race/banner.json new file mode 100644 index 00000000000..55128cf449f --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "zoneid": "99999" +} diff --git a/adapters/engagebdr/engagebdrtest/params/race/native.json b/adapters/engagebdr/engagebdrtest/params/race/native.json new file mode 100644 index 00000000000..7df27654325 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/params/race/native.json @@ -0,0 +1,3 @@ +{ + "zoneid": "99997" +} diff --git a/adapters/engagebdr/engagebdrtest/params/race/video.json b/adapters/engagebdr/engagebdrtest/params/race/video.json new file mode 100644 index 00000000000..b60e65203bf --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "zoneid": "99998" +} diff --git a/adapters/engagebdr/engagebdrtest/supplemental/audio.json b/adapters/engagebdr/engagebdrtest/supplemental/audio.json new file mode 100644 index 00000000000..34cedd6f4db --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/audio.json @@ -0,0 +1,22 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "audio": { + }, + "ext": { + "bidder": { + "sspid": "99996" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "Ignoring imp id=test-imp-id, invalid MediaType. EngageBDR only supports Banner, Video and Native." + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/multi-1-invalid-imp.json b/adapters/engagebdr/engagebdrtest/supplemental/multi-1-invalid-imp.json new file mode 100644 index 00000000000..305d78e294d --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/multi-1-invalid-imp.json @@ -0,0 +1,108 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99999" + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + } + } + ] + }, + "expectedMakeRequestsErrors": [ + "Ignoring imp id=test-imp-id-2, error while decoding impExt, err: unexpected end of JSON input." + ], + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99999", + "body":{ + "id": "test-request-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99999" + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id" : "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": [ + "advertiserdomain.com" + ], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + } + ], + "seat": "test-request-id" + } + ], + "bidid": "test-request-id", + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 9.81, + "adid": "abcde-12345", + "adm": "
", + "adomain": ["advertiserdomain.com"], + "iurl": "http://match.bnmla.com/usersync?sspid=59&redir=", + "cid": "campaign1", + "crid": "abcde-12345", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json new file mode 100644 index 00000000000..a7a44b6a656 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json @@ -0,0 +1,21 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "Ignoring imp id=test-imp-id, error while decoding impExt, err: unexpected end of JSON input." + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext.json b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext.json new file mode 100644 index 00000000000..154f514d390 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext.json @@ -0,0 +1,19 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "Ignoring imp id=test-imp-id, error while decoding extImpBidder, err: unexpected end of JSON input." + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json new file mode 100644 index 00000000000..d9cd3b96697 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json @@ -0,0 +1,23 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "Ignoring imp id=test-imp-id, no sspid present." + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp.json b/adapters/engagebdr/engagebdrtest/supplemental/no-imp.json new file mode 100644 index 00000000000..ff82607d7ab --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/no-imp.json @@ -0,0 +1,12 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + ] + }, + + "expectedMakeRequestsErrors": [ + "Invalid BidRequest. No valid imp." + ] +} + diff --git a/adapters/engagebdr/engagebdrtest/supplemental/response-500.json b/adapters/engagebdr/engagebdrtest/supplemental/response-500.json new file mode 100644 index 00000000000..45338870aa8 --- /dev/null +++ b/adapters/engagebdr/engagebdrtest/supplemental/response-500.json @@ -0,0 +1,49 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid": "99999" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://dsp.bnmla.com/hb?zoneid=99999", + "body":{ + "id": "test-request-id", + "imp": [{ + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "sspid":"99999" + } + } + }] + } + }, + "mockResponse": { + "status": 500 + } + } + ], + "expectedMakeBidsErrors": [ + "Unexpected status code: 500. Run with request.debug = 1 for more info" + ] +} + diff --git a/adapters/engagebdr/params_test.go b/adapters/engagebdr/params_test.go new file mode 100644 index 00000000000..c797d04ecc8 --- /dev/null +++ b/adapters/engagebdr/params_test.go @@ -0,0 +1,50 @@ +package engagebdr + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/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.BidderEngageBDR, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected beachfront 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.BidderEngageBDR, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"sspid":"12345"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"sspid":null}`, + `{"appId":"11bc5dd5-7421-4dd8-c926-40fa653bec76"}`, +} diff --git a/adapters/engagebdr/usersync.go b/adapters/engagebdr/usersync.go new file mode 100644 index 00000000000..ae4047aa89c --- /dev/null +++ b/adapters/engagebdr/usersync.go @@ -0,0 +1,11 @@ +package engagebdr + +import ( + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" + "text/template" +) + +func NewEngageBDRSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("engagebdr", 62, temp, adapters.SyncTypeIframe) +} diff --git a/adapters/engagebdr/usersync_test.go b/adapters/engagebdr/usersync_test.go new file mode 100644 index 00000000000..1e24ff5e2fd --- /dev/null +++ b/adapters/engagebdr/usersync_test.go @@ -0,0 +1,18 @@ +package engagebdr + +import ( + "github.com/stretchr/testify/assert" + "testing" + "text/template" +) + +func TestEngageBDRSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("https://match.bnmla.com/usersync/prbds2s?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r=localhost%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")) + syncer := NewEngageBDRSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo("1", "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA") + assert.NoError(t, err) + assert.Equal(t, "https://match.bnmla.com/usersync/prbds2s?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&r=localhost%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D", syncInfo.URL) + assert.Equal(t, "iframe", syncInfo.Type) + assert.EqualValues(t, 62, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 1329d9a1fe5..c4f5f0efcb1 100644 --- a/config/config.go +++ b/config/config.go @@ -452,6 +452,8 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderEngageBDR, "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") + // openrtb_ext.BidderTappx doesn't have a good default. } @@ -589,6 +591,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.beachfront.platform_id", "155") v.SetDefault("adapters.brightroll.endpoint", "http://east-bid.ybp.yahoo.com/bid/appnexuspbs") v.SetDefault("adapters.consumable.endpoint", "https://e.serverbid.com/api/v2") + v.SetDefault("adapters.engagebdr.endpoint", "http://dsp.bnmla.com/hb") v.SetDefault("adapters.conversant.endpoint", "http://api.hb.ad.cpe.dotomi.com/s2s/header/24") v.SetDefault("adapters.emx_digital.endpoint", "https://hb.emxdgt.com") v.SetDefault("adapters.eplanning.endpoint", "http://ads.us.e-planning.net/hb/1") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 673286cf042..8d56e153b8b 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/emx_digital" + "github.com/prebid/prebid-server/adapters/engagebdr" "github.com/prebid/prebid-server/adapters/eplanning" "github.com/prebid/prebid-server/adapters/gamoshi" "github.com/prebid/prebid-server/adapters/grid" @@ -60,6 +61,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(), openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), openrtb_ext.BidderConsumable: consumable.NewConsumableBidder(cfg.Adapters[string(openrtb_ext.BidderConsumable)].Endpoint), + openrtb_ext.BidderEngageBDR: engagebdr.NewEngageBDRBidder(client, cfg.Adapters[string(openrtb_ext.BidderEngageBDR)].Endpoint), openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 3de7f28e5b8..bd35de4695c 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -56,6 +56,7 @@ const ( BidderYieldmo BidderName = "yieldmo" BidderVisx BidderName = "visx" BidderTappx BidderName = "tappx" + BidderEngageBDR BidderName = "engagebdr" ) // BidderMap stores all the valid OpenRTB 2.x Bidders in the project. This map *must not* be mutated. @@ -96,6 +97,7 @@ var BidderMap = map[string]BidderName{ "yieldmo": BidderYieldmo, "visx": BidderVisx, "tappx": BidderTappx, + "engagebdr": BidderEngageBDR, } // BidderList returns the values of the BidderMap diff --git a/openrtb_ext/imp_engagebdr.go b/openrtb_ext/imp_engagebdr.go new file mode 100644 index 00000000000..14cb444c040 --- /dev/null +++ b/openrtb_ext/imp_engagebdr.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpEngageBDR defines the contract for bidrequest.imp[i].ext.engagebdr +type ExtImpEngageBDR struct { + Sspid string `json:"sspid"` +} diff --git a/static/bidder-info/engagebdr.yaml b/static/bidder-info/engagebdr.yaml new file mode 100644 index 00000000000..d2f7476235f --- /dev/null +++ b/static/bidder-info/engagebdr.yaml @@ -0,0 +1,8 @@ +maintainer: + email: "admin@engagebdr.com" +capabilities: + app: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/engagebdr.json b/static/bidder-params/engagebdr.json new file mode 100644 index 00000000000..4f987004045 --- /dev/null +++ b/static/bidder-params/engagebdr.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "EngageBDR Adapter Params", + "description": "A schema which validates params accepted by the EngageBDR adapter", + "type": "object", + "properties": { + "sspid": { + "type": "string", + "description": "SSPID parameter", + "pattern": "^[0-9]+$" + } + }, + "required": ["sspid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index e78915c93ff..87ecf1b4c0f 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -17,6 +17,7 @@ import ( "github.com/prebid/prebid-server/adapters/consumable" "github.com/prebid/prebid-server/adapters/conversant" "github.com/prebid/prebid-server/adapters/emx_digital" + "github.com/prebid/prebid-server/adapters/engagebdr" "github.com/prebid/prebid-server/adapters/eplanning" "github.com/prebid/prebid-server/adapters/gamoshi" "github.com/prebid/prebid-server/adapters/grid" @@ -66,6 +67,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderFacebook, audienceNetwork.NewFacebookSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderGrid, grid.NewGridSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderGumGum, gumgum.NewGumGumSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderEngageBDR, engagebdr.NewEngageBDRSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderImprovedigital, improvedigital.NewImprovedigitalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 454ac892b1c..2820f19e363 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -49,6 +49,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderGamoshi): syncConfig, string(openrtb_ext.BidderUnruly): syncConfig, string(openrtb_ext.BidderVerizonMedia): syncConfig, + string(openrtb_ext.BidderEngageBDR): syncConfig, }, }