diff --git a/adapters/madsense/madsense.go b/adapters/madsense/madsense.go new file mode 100644 index 00000000000..f5d0fa9df5c --- /dev/null +++ b/adapters/madsense/madsense.go @@ -0,0 +1,126 @@ +package madsense + +import ( + "encoding/json" + "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 +} + +// Builder builds a new instance of the MadSense adapter for the given bidder with the given config. +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) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + reqs := make([]*adapters.RequestData, 0, len(request.Imp)) + var errs []error + + appendReq := func(imps []openrtb2.Imp) { + req, err := a.makeRequest(request, imps) + if err != nil { + errs = append(errs, err) + return + } + if req != nil { + reqs = append(reqs, req) + } + } + + var videoImps, bannerImps []openrtb2.Imp + for _, imp := range request.Imp { + if imp.Banner != nil { + bannerImps = append(bannerImps, imp) + } else if imp.Video != nil { + videoImps = append(videoImps, imp) + } + } + + // we support video podding, so we want to send all video impressions in a single request + appendReq(videoImps) + for _, bannerImp := range bannerImps { + appendReq([]openrtb2.Imp{bannerImp}) + } + + return reqs, errs +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest, imps []openrtb2.Imp) (*adapters.RequestData, error) { + if len(imps) == 0 { + return nil, nil + } + ext, err := parseImpExt(&imps[0]) + if err != nil { + return nil, err + } + + request.Imp = imps + body, err := json.Marshal(request) + if err != nil { + return nil, err + } + + if request.Test == 1 { + ext.CompanyId = "test" + } + + return &adapters.RequestData{ + Method: http.MethodPost, + Uri: a.getEndpointURL(ext), + Body: body, + Headers: getHeaders(request), + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }, nil +} + +func (a *adapter) getEndpointURL(ext *openrtb_ext.ExtImpMadSense) string { + params := url.Values{} + params.Add("company_id", ext.CompanyId) + return a.endpoint + "?" + params.Encode() +} + +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{err} + } + + var resp openrtb2.BidResponse + if err := jsonutil.Unmarshal(response.Body, &resp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Bad Server Response", + }} + } + + var bidErrors []error + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(1) + for _, seatBid := range resp.SeatBid { + for i := range seatBid.Bid { + bid := &seatBid.Bid[i] + typedBid, err := getTypedBidFromBid(bid) + if err != nil { + bidErrors = append(bidErrors, err) + continue + } + bidderResponse.Bids = append(bidderResponse.Bids, typedBid) + } + } + + return bidderResponse, bidErrors +} diff --git a/adapters/madsense/madsense_test.go b/adapters/madsense/madsense_test.go new file mode 100644 index 00000000000..c9792e13921 --- /dev/null +++ b/adapters/madsense/madsense_test.go @@ -0,0 +1,18 @@ +package madsense + +import ( + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "testing" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderMadSense, config.Adapter{ + Endpoint: "https://test.localhost.com"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + adapterstest.RunJSONBidderTest(t, "madsensetest", bidder) +} diff --git a/adapters/madsense/madsensetest/exemplary/banner-app.json b/adapters/madsense/madsensetest/exemplary/banner-app.json new file mode 100644 index 00000000000..bc1b2668fce --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/banner-app.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ip": "1.2.3.4" + }, + "app": { + "bundle": "54321" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "uri": "https://test.localhost.com?company_id=9876543", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ip": "1.2.3.4" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e0", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id", + "adm": "", + "mtype": 1 + } + ] + } + ], + "seat": "madsense" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e0", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ], + "seat": "madsense" + } + ] +} diff --git a/adapters/madsense/madsensetest/exemplary/banner-web.json b/adapters/madsense/madsensetest/exemplary/banner-web.json new file mode 100644 index 00000000000..0ad9c5c8960 --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/banner-web.json @@ -0,0 +1,126 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ipv6": "2001:db8:3333:4444:5555:6666:7777:8888" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page", + "ref": "https://example.com/page" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "2001:db8:3333:4444:5555:6666:7777:8888" + ], + "Referer": [ + "https://example.com/page" + ] + }, + "uri": "https://test.localhost.com?company_id=9876543", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ipv6": "2001:db8:3333:4444:5555:6666:7777:8888" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page", + "ref": "https://example.com/page" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e0", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id", + "adm": "", + "mtype": 1 + } + ] + } + ], + "seat": "madsense" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e0", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ], + "seat": "madsense" + } + ] +} diff --git a/adapters/madsense/madsensetest/exemplary/mixed-imp-types.json b/adapters/madsense/madsensetest/exemplary/mixed-imp-types.json new file mode 100644 index 00000000000..a453fd989ad --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/mixed-imp-types.json @@ -0,0 +1,312 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + } + }, + "impIDs":["test-imp-id","test-imp-id-2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 10, + "adm": "some-test-ad-2", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 30, + "cat": [ + "IAB1-1" + ] + } + ], + "seat": "madsense" + } + ] + } + } + }, + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-3", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + } + }, + "impIDs":["test-imp-id-3"] + }, + "mockResponse": { + "status": 200, + "body": { + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e2", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id-3", + "adm": "", + "mtype": 1 + } + ] + } + ], + "seat": "madsense" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB1-5" + } + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 10, + "adm": "some-test-ad-2", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 30, + "cat": [ + "IAB1-1" + ] + }, + "type": "video", + "video": { + "duration": 30, + "primary_category": "IAB1-1" + } + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "884f46aa-25ab-488a-bd7e-90e3a31a03e2", + "crid": "b83e6e58-9342-4e8c-bf6a-4ad09b231547", + "price": 10, + "impid": "test-imp-id-3", + "adm": "", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/madsense/madsensetest/exemplary/video-app.json b/adapters/madsense/madsensetest/exemplary/video-app.json new file mode 100644 index 00000000000..d3007dc2b83 --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/video-app.json @@ -0,0 +1,141 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + } + ], + "seat": "madsense" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB1-5" + } + } + ] + } + ] +} + diff --git a/adapters/madsense/madsensetest/exemplary/video-web-pod.json b/adapters/madsense/madsensetest/exemplary/video-web-pod.json new file mode 100644 index 00000000000..ce3e354cb60 --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/video-web-pod.json @@ -0,0 +1,212 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + + ], + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + } + }, + "impIDs":["test-imp-id","test-imp-id-2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 10, + "adm": "some-test-ad-2", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 30, + "cat": [ + "IAB1-1" + ] + } + ], + "seat": "madsense" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB1-5" + } + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-2", + "price": 10, + "adm": "some-test-ad-2", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 30, + "cat": [ + "IAB1-1" + ] + }, + "type": "video", + "video": { + "duration": 30, + "primary_category": "IAB1-1" + } + } + ] + } + ] +} + diff --git a/adapters/madsense/madsensetest/exemplary/video-web.json b/adapters/madsense/madsensetest/exemplary/video-web.json new file mode 100644 index 00000000000..bf66a6470fd --- /dev/null +++ b/adapters/madsense/madsensetest/exemplary/video-web.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "site": { + "domain": "domain.com", + "page": "http://domain.com/page" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + } + ], + "seat": "madsense" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB1-5" + } + } + ] + } + ] +} + diff --git a/adapters/madsense/madsensetest/supplemental/bad-response-error.json b/adapters/madsense/madsensetest/supplemental/bad-response-error.json new file mode 100644 index 00000000000..1ce30f8c3f3 --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/bad-response-error.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Bad Server Response", + "comparison": "literal" + } + ] +} + diff --git a/adapters/madsense/madsensetest/supplemental/bad-response-mtype.json b/adapters/madsense/madsensetest/supplemental/bad-response-mtype.json new file mode 100644 index 00000000000..6c9c6c94a67 --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/bad-response-mtype.json @@ -0,0 +1,122 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 4, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + } + ], + "seat": "madsense" + } + ] + } + } + } + ], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [] + }], + "expectedMakeBidsErrors": [ + { + "value": "MType 4 not supported", + "comparison": "literal" + } + ] +} + diff --git a/adapters/madsense/madsensetest/supplemental/bad-response-status-400.json b/adapters/madsense/madsensetest/supplemental/bad-response-status-400.json new file mode 100644 index 00000000000..87424d2f86c --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/bad-response-status-400.json @@ -0,0 +1,98 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ip": "1.2.3.4" + }, + "site": { + "domain": "domain.com", + "page": "page.com/test" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Origin": [ + "domain.com" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "uri": "https://test.localhost.com?company_id=9876543", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 1920, + "h": 1080, + "mimes": [ + "video/x-flv", + "video/mp4" + ] + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ua": "user-agent", + "ip": "1.2.3.4" + }, + "site": { + "domain": "domain.com", + "page": "page.com/test" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/madsense/madsensetest/supplemental/bad-response-status-500.json b/adapters/madsense/madsensetest/supplemental/bad-response-status-500.json new file mode 100644 index 00000000000..143431c7144 --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/bad-response-status-500.json @@ -0,0 +1,92 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 500 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} + diff --git a/adapters/madsense/madsensetest/supplemental/no-content-response.json b/adapters/madsense/madsensetest/supplemental/no-content-response.json new file mode 100644 index 00000000000..27caa245cd3 --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/no-content-response.json @@ -0,0 +1,87 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=9876543", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} + diff --git a/adapters/madsense/madsensetest/supplemental/test-response.json b/adapters/madsense/madsensetest/supplemental/test-response.json new file mode 100644 index 00000000000..9ef7c7894f4 --- /dev/null +++ b/adapters/madsense/madsensetest/supplemental/test-response.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "test": 1, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "app": { + "bundle": "54321" + }, + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "method": "POST", + "uri": "https://test.localhost.com?company_id=test", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "X-Openrtb-Version": [ + "2.6" + ], + "User-Agent": [ + "user-agent" + ], + "X-Forwarded-For": [ + "1.2.3.4" + ] + }, + "body": { + "id": "test-request-id", + "test": 1, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "company_id": "9876543" + } + } + } + ], + "device": { + "ip": "1.2.3.4", + "ua": "user-agent" + }, + "app": { + "bundle": "54321" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + } + ], + "seat": "madsense" + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 10, + "adm": "some-test-ad", + "crid": "54321", + "w": 1920, + "h": 1080, + "mtype": 2, + "dur": 15, + "cat": [ + "IAB1-5", + "IAB1-6" + ] + }, + "type": "video", + "video": { + "duration": 15, + "primary_category": "IAB1-5" + } + } + ] + } + ] +} + diff --git a/adapters/madsense/params_test.go b/adapters/madsense/params_test.go new file mode 100644 index 00000000000..6591767acc2 --- /dev/null +++ b/adapters/madsense/params_test.go @@ -0,0 +1,56 @@ +package madsense + +import ( + "encoding/json" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "testing" +) + +// This file actually intends to test static/bidder-params/madsense.json +// +// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.madsense + +// TestValidParams makes sure that the madsense schema accepts all imp.ext fields which we intend to support. +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.BidderMadSense, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected madsense params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the madsense schema rejects all the imp.ext fields we don't support. +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.BidderMadSense, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"company_id": "9876543"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"companyId": "987654a"}`, + `{"companyId": "98765432"}`, + `{"company_id": ""}`, +} diff --git a/adapters/madsense/utils.go b/adapters/madsense/utils.go new file mode 100644 index 00000000000..7ff97cd65f5 --- /dev/null +++ b/adapters/madsense/utils.go @@ -0,0 +1,99 @@ +package madsense + +import ( + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +func parseImpExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpMadSense, error) { + var bidderExt adapters.ExtImpBidder + + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("Error while decoding extImpBidder, err: %v", err), + } + } + + ext := openrtb_ext.ExtImpMadSense{} + err := jsonutil.Unmarshal(bidderExt.Bidder, &ext) + if err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("Error while decoding impExt, err: %v", err), + } + } + + return &ext, nil +} + +func getHeaders(request *openrtb2.BidRequest) http.Header { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("X-Openrtb-Version", "2.6") + + if request.Device != nil { + if len(request.Device.UA) > 0 { + headers.Add("User-Agent", request.Device.UA) + } + + if len(request.Device.IP) > 0 { + headers.Add("X-Forwarded-For", request.Device.IP) + } + + if len(request.Device.IPv6) > 0 { + headers.Add("X-Forwarded-For", request.Device.IPv6) + } + } + + if request.Site != nil { + if request.Site.Domain != "" { + headers.Add("Origin", request.Site.Domain) + } + if request.Site.Ref != "" { + headers.Set("Referer", request.Site.Ref) + } + } + return headers +} + +func getTypedBidFromBid(bid *openrtb2.Bid) (*adapters.TypedBid, error) { + bidType, err := getMediaTypeForBid(bid) + if err != nil { + return nil, err + } + + var bidVideo *openrtb_ext.ExtBidPrebidVideo + if bidType == openrtb_ext.BidTypeVideo { + bidVideo = &openrtb_ext.ExtBidPrebidVideo{} + if len(bid.Cat) > 0 { + bidVideo.PrimaryCategory = bid.Cat[0] + } + if bid.Dur > 0 { + bidVideo.Duration = int(bid.Dur) + } + } + return &adapters.TypedBid{ + Bid: bid, + BidType: bidType, + BidVideo: bidVideo, + }, nil +} + +func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + default: + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("MType %v not supported", bid.MType), + } + } +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 3867cbde72b..0baf95b2d76 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -134,6 +134,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/loyal" "github.com/prebid/prebid-server/v3/adapters/lunamedia" "github.com/prebid/prebid-server/v3/adapters/mabidder" + "github.com/prebid/prebid-server/v3/adapters/madsense" "github.com/prebid/prebid-server/v3/adapters/madvertise" "github.com/prebid/prebid-server/v3/adapters/marsmedia" "github.com/prebid/prebid-server/v3/adapters/mediago" @@ -371,6 +372,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderLoyal: loyal.Builder, openrtb_ext.BidderLunaMedia: lunamedia.Builder, openrtb_ext.BidderMabidder: mabidder.Builder, + openrtb_ext.BidderMadSense: madsense.Builder, openrtb_ext.BidderMadvertise: madvertise.Builder, openrtb_ext.BidderMarsmedia: marsmedia.Builder, openrtb_ext.BidderMediafuse: appnexus.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 894baf9a3bb..542d3998c59 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -151,6 +151,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderLoyal, BidderLunaMedia, BidderMabidder, + BidderMadSense, BidderMadvertise, BidderMarsmedia, BidderMediafuse, @@ -493,6 +494,7 @@ const ( BidderLoyal BidderName = "loyal" BidderLunaMedia BidderName = "lunamedia" BidderMabidder BidderName = "mabidder" + BidderMadSense BidderName = "madsense" BidderMadvertise BidderName = "madvertise" BidderMarsmedia BidderName = "marsmedia" BidderMediafuse BidderName = "mediafuse" diff --git a/openrtb_ext/imp_madsense.go b/openrtb_ext/imp_madsense.go new file mode 100644 index 00000000000..a0d4c0a6df0 --- /dev/null +++ b/openrtb_ext/imp_madsense.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpMadSense struct { + CompanyId string `json:"company_id"` +} diff --git a/static/bidder-info/madsense.yaml b/static/bidder-info/madsense.yaml new file mode 100644 index 00000000000..4105c65fe98 --- /dev/null +++ b/static/bidder-info/madsense.yaml @@ -0,0 +1,12 @@ +endpoint: "https://ads.madsense.io/pbs" +maintainer: + email: "prebid@madsense.io" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-params/madsense.json b/static/bidder-params/madsense.json new file mode 100644 index 00000000000..54410fd2aee --- /dev/null +++ b/static/bidder-params/madsense.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "madSense Adapter Params", + "description": "A schema which validates params accepted by the madSense adapter", + "type": "object", + "properties": { + "company_id": { + "type": "string", + "description": "An id used to identify madSense company", + "minLength": 1 + } + }, + "required": ["company_id"] +}