From c0025d8d977c53cd0063c1652527379c4ad2d147 Mon Sep 17 00:00:00 2001 From: dtbarne <7635750+dtbarne@users.noreply.github.com> Date: Tue, 26 May 2020 18:45:24 -0500 Subject: [PATCH] MobileFuse Adapter (#1303) Co-authored-by: Dan Barnett --- adapters/mobilefuse/mobilefuse.go | 193 ++++++++++++++++++ adapters/mobilefuse/mobilefuse_test.go | 10 + .../exemplary/multi-format.json | 67 ++++++ .../mobilefusetest/exemplary/multi-imps.json | 110 ++++++++++ .../mobilefusetest/exemplary/no-bid.json | 55 +++++ .../exemplary/optional-params.json | 56 +++++ .../exemplary/simple-banner.json | 93 +++++++++ .../exemplary/simple-video.json | 96 +++++++++ .../mobilefusetest/params/race/banner.json | 5 + .../mobilefusetest/params/race/video.json | 5 + .../supplemental/bad-ext-bidder.json | 36 ++++ .../mobilefusetest/supplemental/bad-ext.json | 32 +++ .../supplemental/bad-status-code.json | 62 ++++++ .../mobilefusetest/supplemental/no-imps.json | 27 +++ .../supplemental/server-error-response.json | 62 ++++++ adapters/mobilefuse/params_test.go | 54 +++++ config/config.go | 1 + exchange/adapter_map.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_mobilefuse.go | 8 + static/bidder-info/mobilefuse.yaml | 7 + static/bidder-params/mobilefuse.json | 24 +++ usersync/usersyncers/syncer_test.go | 7 +- 23 files changed, 1011 insertions(+), 3 deletions(-) create mode 100644 adapters/mobilefuse/mobilefuse.go create mode 100644 adapters/mobilefuse/mobilefuse_test.go create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json create mode 100644 adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json create mode 100644 adapters/mobilefuse/mobilefusetest/params/race/banner.json create mode 100644 adapters/mobilefuse/mobilefusetest/params/race/video.json create mode 100644 adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json create mode 100644 adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json create mode 100644 adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json create mode 100644 adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json create mode 100644 adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json create mode 100644 adapters/mobilefuse/params_test.go create mode 100644 openrtb_ext/imp_mobilefuse.go create mode 100644 static/bidder-info/mobilefuse.yaml create mode 100644 static/bidder-params/mobilefuse.json diff --git a/adapters/mobilefuse/mobilefuse.go b/adapters/mobilefuse/mobilefuse.go new file mode 100644 index 00000000000..3351f10d45b --- /dev/null +++ b/adapters/mobilefuse/mobilefuse.go @@ -0,0 +1,193 @@ +package mobilefuse + +import ( + "encoding/json" + "fmt" + "github.com/golang/glog" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "strconv" + "text/template" +) + +type MobileFuseAdapter struct { + EndpointTemplate template.Template +} + +func NewMobileFuseBidder(endpointTemplate string) adapters.Bidder { + parsedTemplate, err := template.New("endpointTemplate").Parse(endpointTemplate) + + if err != nil { + glog.Fatal("Unable parse endpoint template: " + err.Error()) + return nil + } + + return &MobileFuseAdapter{EndpointTemplate: *parsedTemplate} +} + +func (adapter *MobileFuseAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var adapterRequests []*adapters.RequestData + + adapterRequest, errs := adapter.makeRequest(request) + + if errs == nil { + adapterRequests = append(adapterRequests, adapterRequest) + } + + return adapterRequests, errs +} + +func (adapter *MobileFuseAdapter) MakeBids(incomingRequest *openrtb.BidRequest, outgoingRequest *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.", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode), + }} + } + + var incomingBidResponse openrtb.BidResponse + + if err := json.Unmarshal(response.Body, &incomingBidResponse); err != nil { + return nil, []error{err} + } + + outgoingBidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + + for _, seatbid := range incomingBidResponse.SeatBid { + for i := range seatbid.Bid { + outgoingBidResponse.Bids = append(outgoingBidResponse.Bids, &adapters.TypedBid{ + Bid: &seatbid.Bid[i], + BidType: adapter.getBidType(seatbid.Bid[i].ImpID, incomingRequest.Imp), + }) + } + } + + return outgoingBidResponse, nil +} + +func (adapter *MobileFuseAdapter) makeRequest(bidRequest *openrtb.BidRequest) (*adapters.RequestData, []error) { + var errs []error + + mobileFuseExtension, errs := adapter.getFirstMobileFuseExtension(bidRequest) + + if errs != nil { + return nil, errs + } + + endpoint, err := adapter.getEndpoint(mobileFuseExtension) + + if err != nil { + return nil, append(errs, err) + } + + validImps := adapter.getValidImps(bidRequest, mobileFuseExtension) + + if len(validImps) == 0 { + err := fmt.Errorf("No valid imps") + errs = append(errs, err) + return nil, errs + } + + mobileFuseBidRequest := *bidRequest + mobileFuseBidRequest.Imp = validImps + body, err := json.Marshal(mobileFuseBidRequest) + + if err != nil { + return nil, append(errs, err) + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return &adapters.RequestData{ + Method: "POST", + Uri: endpoint, + Body: body, + Headers: headers, + }, errs +} + +func (adapter *MobileFuseAdapter) getFirstMobileFuseExtension(request *openrtb.BidRequest) (*openrtb_ext.ExtImpMobileFuse, []error) { + var mobileFuseImpExtension openrtb_ext.ExtImpMobileFuse + var errs []error + + for _, imp := range request.Imp { + var bidder_imp_extension adapters.ExtImpBidder + + err := json.Unmarshal(imp.Ext, &bidder_imp_extension) + + if err != nil { + errs = append(errs, err) + continue + } + + err = json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension) + + if err != nil { + errs = append(errs, err) + continue + } + + break + } + + return &mobileFuseImpExtension, errs +} + +func (adapter *MobileFuseAdapter) getEndpoint(ext *openrtb_ext.ExtImpMobileFuse) (string, error) { + publisher_id := strconv.Itoa(ext.PublisherId) + + url, errs := macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{PublisherID: publisher_id}) + + if errs != nil { + return "", errs + } + + if ext.TagidSrc == "ext" { + url += "&tagid_src=ext" + } + + return url, nil +} + +func (adapter *MobileFuseAdapter) getValidImps(bidRequest *openrtb.BidRequest, ext *openrtb_ext.ExtImpMobileFuse) []openrtb.Imp { + var validImps []openrtb.Imp + + for _, imp := range bidRequest.Imp { + if imp.Banner != nil || imp.Video != nil { + if imp.Banner != nil && imp.Video != nil { + imp.Video = nil + } + + imp.TagID = strconv.Itoa(ext.PlacementId) + imp.Ext = nil + validImps = append(validImps, imp) + + break + } + } + + return validImps +} + +func (adapter *MobileFuseAdapter) getBidType(imp_id string, imps []openrtb.Imp) openrtb_ext.BidType { + if imps[0].Video != nil { + return openrtb_ext.BidTypeVideo + } + + return openrtb_ext.BidTypeBanner +} diff --git a/adapters/mobilefuse/mobilefuse_test.go b/adapters/mobilefuse/mobilefuse_test.go new file mode 100644 index 00000000000..15021a06c35 --- /dev/null +++ b/adapters/mobilefuse/mobilefuse_test.go @@ -0,0 +1,10 @@ +package mobilefuse + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "mobilefusetest", NewMobileFuseBidder("http://mfx-us-east.mobilefuse.com/openrtb?pub_id={{.PublisherID}}")) +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json b/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json new file mode 100644 index 00000000000..49fb88196ed --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/multi-format.json @@ -0,0 +1,67 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "placement_id": 123456, + "pub_id": 1234 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "tagid": "123456" + } + ] + } + }, + + "mockResponse": { + "status": 204, + "body": "" + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json b/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json new file mode 100644 index 00000000000..61ac1a3e7bb --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/multi-imps.json @@ -0,0 +1,110 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 123456, + "pub_id": 1234 + } + } + }, + { + "id": "2", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 234567, + "pub_id": 1234 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "tagid": "123456" + } + ] + } + }, + + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mobilefuse", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json b/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json new file mode 100644 index 00000000000..cec47a0b173 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/no-bid.json @@ -0,0 +1,55 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 999999, + "pub_id": 1111 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "999999" + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json b/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json new file mode 100644 index 00000000000..2f71da8f7f6 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/optional-params.json @@ -0,0 +1,56 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 999999, + "pub_id": 1111, + "tagid_src": "ext" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111&tagid_src=ext", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "999999" + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json b/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json new file mode 100644 index 00000000000..7db7776d6bf --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/simple-banner.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 123456, + "pub_id": 1234 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1234", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "tagid": "123456" + } + ] + } + }, + + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mobilefuse", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json b/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json new file mode 100644 index 00000000000..118c82e1c18 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/exemplary/simple-video.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "placement_id": 200000, + "pub_id": 2000 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=2000", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "tagid": "200000" + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "mobilefuse", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 2.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 480 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 2.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 480 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/mobilefuse/mobilefusetest/params/race/banner.json b/adapters/mobilefuse/mobilefusetest/params/race/banner.json new file mode 100644 index 00000000000..8c8d3f2b90c --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "placement_id": 123456, + "pub_id": 1234, + "tagid_src": "ext" +} diff --git a/adapters/mobilefuse/mobilefusetest/params/race/video.json b/adapters/mobilefuse/mobilefusetest/params/race/video.json new file mode 100644 index 00000000000..8c8d3f2b90c --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/params/race/video.json @@ -0,0 +1,5 @@ +{ + "placement_id": 123456, + "pub_id": 1234, + "tagid_src": "ext" +} diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json new file mode 100644 index 00000000000..61cf115faea --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext-bidder.json @@ -0,0 +1,36 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpMobileFuse", + "comparison": "literal" + } + ], + + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": "aaa" + } + } + ] + }, + + "httpCalls": [], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json new file mode 100644 index 00000000000..f0c23aa27e3 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-ext.json @@ -0,0 +1,32 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ], + + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": "aaa" + } + ] + }, + + "httpCalls": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json b/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json new file mode 100644 index 00000000000..5ee9468085d --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/supplemental/bad-status-code.json @@ -0,0 +1,62 @@ +{ + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400.", + "comparison": "literal" + } + ], + + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 999999, + "pub_id": 1111 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "999999" + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": "" + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json b/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json new file mode 100644 index 00000000000..eab96d4df67 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/supplemental/no-imps.json @@ -0,0 +1,27 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "No valid imps", + "comparison": "literal" + } + ], + + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "ext": { + "bidder": { + "placement_id": 999999, + "pub_id": 1111 + } + } + } + ] + }, + + "httpCalls": [], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json b/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json new file mode 100644 index 00000000000..25c4a0bd467 --- /dev/null +++ b/adapters/mobilefuse/mobilefusetest/supplemental/server-error-response.json @@ -0,0 +1,62 @@ +{ + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500.", + "comparison": "literal" + } + ], + + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placement_id": 999999, + "pub_id": 1111 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://mfx-us-east.mobilefuse.com/openrtb?pub_id=1111", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "tagid": "999999" + } + ] + } + }, + "mockResponse": { + "status": 500, + "body": "" + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/mobilefuse/params_test.go b/adapters/mobilefuse/params_test.go new file mode 100644 index 00000000000..dbfd8894e70 --- /dev/null +++ b/adapters/mobilefuse/params_test.go @@ -0,0 +1,54 @@ +package mobilefuse + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestValidParams(test *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + + if err != nil { + test.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + err := validator.Validate(openrtb_ext.BidderMobileFuse, json.RawMessage(validParam)) + + if err != nil { + test.Errorf("Schema rejected MobileFuse params: %s\nError: %v", validParam, err) + } + } +} + +func TestInvalidParams(test *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + + if err != nil { + test.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + err := validator.Validate(openrtb_ext.BidderMobileFuse, json.RawMessage(invalidParam)) + + if err == nil { + test.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placement_id":123,"pub_id":456}`, + `{"placement_id":123,"pub_id":456,"tagid_src":"ext"}`, + `{"placement_id":123, "pub_id":456, "tagid_src":""}`, +} + +var invalidParams = []string{ + `{"placement_id":123}`, + `{"pub_id":456}`, + `{"placement_id":"123","pub_id":"456"}`, + `{"placement_id":123, "placementId":123}`, + `{"tagid_src":"ext"}`, +} diff --git a/config/config.go b/config/config.go index beb6e0a6844..703606b2824 100755 --- a/config/config.go +++ b/config/config.go @@ -729,6 +729,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.lunamedia.endpoint", "http://api.lunamedia.io/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet") v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/") + v.SetDefault("adapters.mobilefuse.endpoint", "http://mfx-us-east.mobilefuse.com/openrtb?pub_id={{.PublisherID}}") v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs") v.SetDefault("adapters.ninthdecimal.endpoint", "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}") v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 9110b20ffff..15e57f55105 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -44,6 +44,7 @@ import ( "github.com/prebid/prebid-server/adapters/lunamedia" "github.com/prebid/prebid-server/adapters/marsmedia" "github.com/prebid/prebid-server/adapters/mgid" + "github.com/prebid/prebid-server/adapters/mobilefuse" "github.com/prebid/prebid-server/adapters/nanointeractive" "github.com/prebid/prebid-server/adapters/ninthdecimal" "github.com/prebid/prebid-server/adapters/openx" @@ -121,6 +122,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderLunaMedia: lunamedia.NewLunaMediaBidder(cfg.Adapters[string(openrtb_ext.BidderLunaMedia)].Endpoint), openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint), openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint), + openrtb_ext.BidderMobileFuse: mobilefuse.NewMobileFuseBidder(cfg.Adapters[string(openrtb_ext.BidderMobileFuse)].Endpoint), openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint), openrtb_ext.BidderNinthDecimal: ninthdecimal.NewNinthDecimalBidder(cfg.Adapters[string(openrtb_ext.BidderNinthDecimal)].Endpoint), openrtb_ext.BidderOrbidder: orbidder.NewOrbidderBidder(cfg.Adapters[string(openrtb_ext.BidderOrbidder)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index a0a8c6a8929..7db59e69a4f 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -62,6 +62,7 @@ const ( BidderLunaMedia BidderName = "lunamedia" BidderMarsmedia BidderName = "marsmedia" BidderMgid BidderName = "mgid" + BidderMobileFuse BidderName = "mobilefuse" BidderNanoInteractive BidderName = "nanointeractive" BidderNinthDecimal BidderName = "ninthdecimal" BidderOpenx BidderName = "openx" @@ -135,6 +136,7 @@ var BidderMap = map[string]BidderName{ "lunamedia": BidderLunaMedia, "marsmedia": BidderMarsmedia, "mgid": BidderMgid, + "mobilefuse": BidderMobileFuse, "nanointeractive": BidderNanoInteractive, "ninthdecimal": BidderNinthDecimal, "openx": BidderOpenx, diff --git a/openrtb_ext/imp_mobilefuse.go b/openrtb_ext/imp_mobilefuse.go new file mode 100644 index 00000000000..ea53c5914f1 --- /dev/null +++ b/openrtb_ext/imp_mobilefuse.go @@ -0,0 +1,8 @@ +package openrtb_ext + +// ExtImpMobileFuse defines the contract for bidrequest.imp[i].ext.mobilefuse +type ExtImpMobileFuse struct { + PlacementId int `json:"placement_id"` + PublisherId int `json:"pub_id"` + TagidSrc string `json:"tagid_src"` +} diff --git a/static/bidder-info/mobilefuse.yaml b/static/bidder-info/mobilefuse.yaml new file mode 100644 index 00000000000..178e407d927 --- /dev/null +++ b/static/bidder-info/mobilefuse.yaml @@ -0,0 +1,7 @@ +maintainer: + email: prebid@mobilefuse.com +capabilities: + app: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/mobilefuse.json b/static/bidder-params/mobilefuse.json new file mode 100644 index 00000000000..15f17148072 --- /dev/null +++ b/static/bidder-params/mobilefuse.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "MobileFuse Adapter Params", + "description": "A schema which validates params accepted by the MobileFuse adapter", + "type": "object", + "properties": { + "placement_id": { + "type": "integer", + "description": "An ID which identifies this specific inventory placement" + }, + "pub_id": { + "type": "integer", + "description": "An ID which identifies the publisher selling the inventory." + }, + "tagid_src": { + "type": "string", + "description": "ext if passing publisher's ids, empty if passing MobileFuse IDs in placement_id field. Defaults to empty" + } + }, + "required": [ + "placement_id", + "pub_id" + ] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 49ce85d0910..f448da50005 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -80,12 +80,13 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderAdgeneration: true, openrtb_ext.BidderAdoppler: true, openrtb_ext.BidderApplogy: true, - openrtb_ext.BidderTappx: true, + openrtb_ext.BidderKidoz: true, openrtb_ext.BidderKubient: true, + openrtb_ext.BidderMobileFuse: true, + openrtb_ext.BidderOrbidder: true, openrtb_ext.BidderPubnative: true, - openrtb_ext.BidderKidoz: true, + openrtb_ext.BidderTappx: true, openrtb_ext.BidderYeahmobi: true, - openrtb_ext.BidderOrbidder: true, } for bidder, config := range cfg.Adapters {