diff --git a/adapters/mediasquare/mediasquare.go b/adapters/mediasquare/mediasquare.go new file mode 100644 index 00000000000..c23164c254a --- /dev/null +++ b/adapters/mediasquare/mediasquare.go @@ -0,0 +1,131 @@ +package mediasquare + +import ( + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +type adapter struct { + endpoint string +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + return &adapter{ + endpoint: config.Endpoint, + }, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var ( + requestData []*adapters.RequestData + errs []error + ) + if request == nil || request.Imp == nil { + errs = append(errs, errorWriter(" request", nil, true)) + return nil, errs + } + + msqParams := initMsqParams(request) + msqParams.Test = (request.Test == int8(1)) + for _, imp := range request.Imp { + var ( + bidderExt adapters.ExtImpBidder + msqExt openrtb_ext.ImpExtMediasquare + currentCode = msqParametersCodes{ + AdUnit: imp.TagID, + AuctionId: request.ID, + BidId: imp.ID, + } + ) + + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, errorWriter(" imp[ext]", err, len(imp.Ext) == 0)) + continue + } + if err := jsonutil.Unmarshal(bidderExt.Bidder, &msqExt); err != nil { + errs = append(errs, errorWriter(" imp-bidder[ext]", err, len(bidderExt.Bidder) == 0)) + continue + } + currentCode.Owner = msqExt.Owner + currentCode.Code = msqExt.Code + + if currentCode.setContent(imp) { + msqParams.Codes = append(msqParams.Codes, currentCode) + } + } + + req, err := a.makeRequest(request, &msqParams) + if err != nil { + errs = append(errs, err) + } else if req != nil { + requestData = append(requestData, req) + } + return requestData, errs +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest, msqParams *msqParameters) (requestData *adapters.RequestData, err error) { + var requestJsonBytes []byte + if msqParams == nil { + err = errorWriter(" msqParams", nil, true) + return + } + if requestJsonBytes, err = jsonutil.Marshal(msqParams); err == nil { + var headers http.Header = headerList + requestData = &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: requestJsonBytes, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + } + } else { + err = errorWriter(" jsonutil.Marshal", err, false) + } + + return +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var ( + bidderResponse *adapters.BidderResponse + errs []error + ) + if response.StatusCode != http.StatusOK { + switch response.StatusCode { + case http.StatusBadRequest: + errs = []error{&errortypes.BadInput{Message: fmt.Sprintf(" Unexpected status code: %d.", response.StatusCode)}} + default: + errs = []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf(" Unexpected status code: %d. Run with request.debug = 1 for more info.", response.StatusCode), + }} + } + return bidderResponse, errs + } + + var msqResp msqResponse + if err := jsonutil.Unmarshal(response.Body, &msqResp); err != nil { + errs = []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf(" Unexpected status code: %d. Bad server response: %s.", + http.StatusNotAcceptable, err.Error())}, + } + return bidderResponse, errs + } + if len(msqResp.Responses) == 0 { + errs = []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf(" Unexpected status code: %d. No responses found into body content.", + http.StatusNoContent)}, + } + return bidderResponse, errs + } + bidderResponse = adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + msqResp.getContent(bidderResponse) + + return bidderResponse, errs +} diff --git a/adapters/mediasquare/mediasquare_test.go b/adapters/mediasquare/mediasquare_test.go new file mode 100644 index 00000000000..091f13ba190 --- /dev/null +++ b/adapters/mediasquare/mediasquare_test.go @@ -0,0 +1,44 @@ +package mediasquare + +import ( + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestBidderMediasquare(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderMediasquare, config.Adapter{ + Endpoint: "https://pbs-front.mediasquare.fr/msq_prebid"}, + config.Server{ExternalUrl: "https://pbs-front.mediasquare.fr/msq_prebid", GvlID: 1, DataCenter: "2"}) + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + adapterstest.RunJSONBidderTest(t, "mediasquaretest", bidder) +} + +func TestMakeRequests(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderMediasquare, config.Adapter{ + Endpoint: "https://pbs-front.mediasquare.fr/msq_prebid"}, + config.Server{ExternalUrl: "https://pbs-front.mediasquare.fr/msq_prebid", GvlID: 1, DataCenter: "2"}) + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + // MakeRequests : case request is empty. + resp, errs := bidder.MakeRequests(nil, nil) + expectingErrors := []error{errorWriter(" request", nil, true)} + assert.Equal(t, []*adapters.RequestData(nil), resp, "resp, was supposed to be empty result.") + assert.Equal(t, expectingErrors, errs, "errs, was supposed to be :", expectingErrors) + + // MakeRequests : case request.Imp is empty. + bidResquest := openrtb2.BidRequest{ID: "id-test", Imp: nil} + resp, errs = bidder.MakeRequests(&bidResquest, nil) + expectingErrors = []error{errorWriter(" request", nil, true)} + assert.Equal(t, []*adapters.RequestData(nil), resp, "resp, was supposed to be empty result.") + assert.Equal(t, expectingErrors, errs, "errs, was supposed to be :", expectingErrors) +} diff --git a/adapters/mediasquare/mediasquaretest/exemplary/multi-format.json b/adapters/mediasquare/mediasquaretest/exemplary/multi-format.json new file mode 100644 index 00000000000..335da6ab6cc --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/exemplary/multi-format.json @@ -0,0 +1,444 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "bidfloorcur": "USD", + "banner": { + "w": 970, + "h": 250 + }, + "ext": { + "bidder": { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_pave" + } + } + }, + { + "id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "bidfloor": 0.01, + "bidfloorcur": "USD", + "banner": { + "format": [ + { "w": 300, "h": 250 }, + { "w": 300, "h": 600 }, + { "w": 120, "h": 600 } + ] + }, + "ext": { + "bidder": { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_pave" + } + } + }, + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "bidfloor": 1, + "bidfloorcur": "USD", + "tagid": "msq_tag_200123_native", + "banner": null, + "video": null, + "native": { + "request": "request-test", + "ext": { + "title": { "required": true, "len": 80 }, + "body": { "required": true }, + "icon": { + "required": false, + "aspect_ratio": { + "min_width": 50, + "min_height": 50, + "ratio_width": 2, + "ratio_height": 3 + } + }, + "image": { + "required": false, + "aspect_ratio": { + "min_width": 300, + "min_height": 200, + "ratio_width": 2, + "ratio_height": 3 + } + }, + "clickUrl": { "required": true }, + "sizes": [ + [970, 250], + [728, 90] + ] + } + }, + "ext": { + "bidder": { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_pave" + } + } + }, + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456d9", + "bidfloor": 1, + "tagid": "msq_tag_200123_native", + "banner": null, + "native": null, + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + }, + "ext": { + "bidder": { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_video" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": "mediasquare-test" + } + }, + "user": { + "consent": "there-is-a-real-cs-in-it" + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "adunit": "", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "code": "publishername_atf_desktop_rg_pave", + "owner": "msq_test", + "mediatypes": { + "banner": { + "sizes": [[970, 250]] + } + }, + "floor": { + "970x250": { + "floor": 1, + "currency": "USD" + } + } + }, + { + "adunit": "", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "code": "publishername_atf_desktop_rg_pave", + "owner": "msq_test", + "mediatypes": { + "banner": { + "sizes": [ + [300, 250], + [300, 600], + [120, 600] + ] + } + }, + "floor": { + "120x600": { + "floor": 0.01, + "currency": "USD" + }, + "300x250": { + "floor": 0.01, + "currency": "USD" + }, + "300x600": { + "floor": 0.01, + "currency": "USD" + } + } + }, + { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_pave", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "mediatypes": { + "native_request": "request-test" + }, + "floor": { + "*": { "floor": 1, "currency": "USD" } + } + }, + { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_video", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456d9", + "mediatypes": { + "video": { + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + }, + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1 + } + }, + "floor": { + "*": { "floor": 1 }, + "800x600": { "floor": 1 } + } + } + ], + "gdpr": { + "consent_required": true, + "consent_string": "there-is-a-real-cs-in-it" + }, + "dsa": "mediasquare-test", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "type": "pbs", + "test": true + }, + "impIDs": [ + "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "2c35e25e-e7d3-41bf-b810-06a449f456d9" + ] + }, + + "mockResponse": { + "status": 200, + "body": { + "infos": { + "version": "1.6.1", + "description": "mediasquare prebid client endpoint" + }, + "cookies": null, + "responses": [ + { + "ad": "\u003c!-- This is an example --\u003e", + "bid_id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 2, + "increment": 2, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "width": 250, + "net_revenue": true, + "transaction_id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "ttl": 20000, + "adomain": ["mediasquare.fr"], + "hasConsent": true + }, + { + "ad": "\u003c!-- This is an example --\u003e", + "bid_id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 0.02, + "increment": 0.02, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "width": 250, + "net_revenue": true, + "transaction_id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "ttl": 20000, + "adomain": ["mediasquare.fr"], + "hasConsent": true + }, + { + "bid_id": "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 2, + "increment": 2, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "net_revenue": true, + "transaction_id": "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "ttl": 20000, + "native": { + "clickUrl": "http: //i.am.a/URL", + "title": "Learn about this awesome thing" + }, + "adomain": ["mediasquare.fr"], + "hasConsent": true + }, + { + "bid_id": "2c35e25e-e7d3-41bf-b810-06a449f456d9", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_video", + "cpm": 2, + "increment": 2, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "net_revenue": true, + "transaction_id": "2c35e25e-e7d3-41bf-b810-06a449f456d9", + "ttl": 20000, + "video": { + "xml": "Some Xml Vast", + "url": "https://dummy.domain.tv/some_vast" + }, + "adomain": ["mediasquare.fr"], + "hasConsent": true + } + ], + "Calc_cpm": { + "2059a3e6-71a3-43ea-8290-b5ceb13d35a8": { + "TmpCpmMax": 0, + "CpmMax": 0.02 + }, + "2c35e25e-e7d3-41bf-b810-06a449f456b9": { + "TmpCpmMax": 0, + "CpmMax": 2 + }, + "2c35e25e-e7d3-41bf-b810-06a449f456c9": { + "TmpCpmMax": 0, + "CpmMax": 2 + }, + "2c35e25e-e7d3-41bf-b810-06a449f456d9": { + "TmpCpmMax": 0, + "CpmMax": 2 + } + } + } + } + } + ], + "expectedBidResponses": [ + { + "Currency": "USD", + "Bids": [ + { + "Bid": { + "id": "", + "impid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "price": 2, + "adm": "\u003c!-- This is an example --\u003e", + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "w": 250, + "mtype": 1 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "banner" + }, + "type": "banner" + }, + { + "Bid": { + "id": "", + "impid": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "price": 0.02, + "adm": "\u003c!-- This is an example --\u003e", + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "w": 250, + "mtype": 1 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "banner" + }, + "type": "banner" + }, + { + "Bid": { + "id": "", + "impid": "2c35e25e-e7d3-41bf-b810-06a449f456c9", + "price": 2, + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "mtype": 4 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "native" + }, + "type": "native" + }, + { + "Bid": { + "id": "", + "impid": "2c35e25e-e7d3-41bf-b810-06a449f456d9", + "price": 2, + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "mtype": 2 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "video" + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/exemplary/simple-banner.json b/adapters/mediasquare/mediasquaretest/exemplary/simple-banner.json new file mode 100644 index 00000000000..8769b37df15 --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/exemplary/simple-banner.json @@ -0,0 +1,258 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "bidfloorcur": "USD", + "banner": { + "w": 970, + "h": 250 + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_pave" + } + } + }, + { + "id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "bidfloor": 0.01, + "bidfloorcur": "USD", + "banner": { + "format": [ + { "w": 300, "h": 250 }, + { "w": 300, "h": 600 }, + { "w": 120, "h": 600 } + ] + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_pave" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": "mediasquare-test" + } + }, + "user": { + "consent": "there-is-a-real-cs-in-it" + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "adunit": "", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "code": "publishername_atf_desktop_rg_pave", + "owner": "test", + "mediatypes": { + "banner": { + "sizes": [[970, 250]] + } + }, + "floor": { + "970x250": { + "floor": 1, + "currency": "USD" + } + } + }, + { + "adunit": "", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "code": "publishername_atf_desktop_rg_pave", + "owner": "test", + "mediatypes": { + "banner": { + "sizes": [ + [300, 250], + [300, 600], + [120, 600] + ] + } + }, + "floor": { + "120x600": { + "floor": 0.01, + "currency": "USD" + }, + "300x250": { + "floor": 0.01, + "currency": "USD" + }, + "300x600": { + "floor": 0.01, + "currency": "USD" + } + } + } + ], + "gdpr": { + "consent_required": true, + "consent_string": "there-is-a-real-cs-in-it" + }, + "dsa": "mediasquare-test", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "type": "pbs", + "test": true + }, + "impIDs": [ + "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "2059a3e6-71a3-43ea-8290-b5ceb13d35a8" + ] + }, + + "mockResponse": { + "status": 200, + "body": { + "infos": { + "version": "1.6.1", + "description": "mediasquare prebid client endpoint" + }, + "cookies": null, + "responses": [ + { + "ad": "\u003c!-- This is an example --\u003e", + "bid_id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 2, + "increment": 2, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "width": 250, + "net_revenue": true, + "transaction_id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "ttl": 20000, + "adomain": ["mediasquare.fr"], + "dsa": "dsa-mediasquare", + "hasConsent": true + }, + { + "ad": "\u003c!-- This is an example --\u003e", + "bid_id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 0.02, + "increment": 0.02, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "width": 250, + "net_revenue": true, + "transaction_id": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "ttl": 20000, + "adomain": ["mediasquare.fr"], + "hasConsent": true + } + ], + "Calc_cpm": { + "2059a3e6-71a3-43ea-8290-b5ceb13d35a8": { + "TmpCpmMax": 0, + "CpmMax": 0.02 + }, + "2c35e25e-e7d3-41bf-b810-06a449f456b9": { + "TmpCpmMax": 0, + "CpmMax": 2 + } + } + } + } + } + ], + "expectedBidResponses": [ + { + "Currency": "USD", + "Bids": [ + { + "Bid": { + "id": "", + "impid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "price": 2, + "adm": "\u003c!-- This is an example --\u003e", + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "w": 250, + "mtype": 1 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "banner" + }, + "type": "banner" + }, + { + "Bid": { + "id": "", + "impid": "2059a3e6-71a3-43ea-8290-b5ceb13d35a8", + "price": 0.02, + "adm": "\u003c!-- This is an example --\u003e", + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "w": 250, + "mtype": 1 + }, + "BidMeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "banner" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/exemplary/simple-native.json b/adapters/mediasquare/mediasquaretest/exemplary/simple-native.json new file mode 100644 index 00000000000..9a9e842b344 --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/exemplary/simple-native.json @@ -0,0 +1,163 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "bidfloorcur": "USD", + "tagid": "msq_tag_200123_native", + "banner": null, + "video": null, + "native": { + "request": "request-test" + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_pave" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": "mediasquare-test" + } + }, + "user": { + "consent": "there-is-a-real-cs-in-it" + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "owner": "test", + "code": "publishername_atf_desktop_rg_pave", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "mediatypes": { + "native_request": "request-test" + }, + "floor": { + "*": { "floor": 1, "currency": "USD" } + } + } + ], + "gdpr": { + "consent_string": "there-is-a-real-cs-in-it", + "consent_required": true + }, + "type": "pbs", + "dsa": "mediasquare-test", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "test": true + }, + "impIDs": ["2c35e25e-e7d3-41bf-b810-06a449f456b9"] + }, + + "mockResponse": { + "status": 200, + "body": { + "infos": { + "version": "1.6.1", + "description": "mediasquare prebid client endpoint" + }, + "cookies": null, + "responses": [ + { + "bid_id": "4e2d4580c1da1", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 1, + "increment": 1, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "net_revenue": true, + "transaction_id": "4e2d4580c1da1", + "ttl": 20000, + "native": { + "clickUrl": "http: //i.am.a/URL", + "title": "Learn about this awesome thing" + }, + "adomain": ["mediasquare.fr"], + "hasConsent": true + } + ], + "Calc_cpm": { "4e2d4580c1da1": { "TmpCpmMax": 0, "CpmMax": 1 } } + } + } + } + ], + "expectedBidResponses": [ + { + "Currency": "USD", + "Bids": [ + { + "bid": { + "id": "", + "impid": "4e2d4580c1da1", + "price": 1, + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "mtype": 4 + }, + "bidmeta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "native" + }, + "type": "native", + "bidvideo": null, + "dealpriority": 0 + } + ] + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/exemplary/simple-video.json b/adapters/mediasquare/mediasquaretest/exemplary/simple-video.json new file mode 100644 index 00000000000..13de7f873b5 --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/exemplary/simple-video.json @@ -0,0 +1,192 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "tagid": "msq_tag_200123_native", + "banner": null, + "native_request": null, + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_video" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": "mediasquare-test" + } + }, + "user": { + "consent": "there-is-a-real-cs-in-it" + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "owner": "test", + "code": "publishername_atf_desktop_rg_video", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "mediatypes": { + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + } + }, + "floor": { + "*": { "floor": 1 }, + "800x600": { "floor": 1 } + } + } + ], + "gdpr": { + "consent_string": "there-is-a-real-cs-in-it", + "consent_required": true + }, + "type": "pbs", + "dsa": "mediasquare-test", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "test": true + }, + "impIDs": ["2c35e25e-e7d3-41bf-b810-06a449f456b9"] + }, + + "mockResponse": { + "status": 200, + "body": { + "infos": { + "version": "1.6.1", + "description": "mediasquare prebid client endpoint" + }, + "cookies": null, + "responses": [ + { + "bid_id": "4e2d4580c1da1", + "bidder": "msq_test", + "code": "test/publishername_atf_desktop_rg_pave", + "cpm": 1, + "increment": 1, + "currency": "USD", + "creative_id": "msq_test|fakeCreative", + "net_revenue": true, + "transaction_id": "4e2d4580c1da1", + "ttl": 20000, + "video": { + "xml": "Some Xml Vast", + "url": "https://dummy.domain.tv/some_vast" + }, + "adomain": ["mediasquare.fr"], + "dsa": { + "behalf": "dsa-test-behalf", + "paid": "dsa-test-paid" + }, + "hasConsent": true + } + ], + "Calc_cpm": { "4e2d4580c1da1": { "TmpCpmMax": 0, "CpmMax": 1 } } + } + } + } + ], + "expectedBidResponses": [ + { + "Currency": "USD", + "Bids": [ + { + "bid": { + "id": "", + "impid": "4e2d4580c1da1", + "price": 1, + "adomain": ["mediasquare.fr"], + "crid": "msq_test|fakeCreative", + "mtype": 2, + "ext": { + "dsa": { "behalf": "dsa-test-behalf", "paid": "dsa-test-paid" } + } + }, + "meta": { + "advertiserDomains": ["mediasquare.fr"], + "mediaType": "video" + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/supplemental/no-valid-imp-ext.json b/adapters/mediasquare/mediasquaretest/supplemental/no-valid-imp-ext.json new file mode 100644 index 00000000000..6fae03fd4e2 --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/supplemental/no-valid-imp-ext.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "id-ok", + "imp": [ + { "id": "0" }, + { "id": "1", "ext": { "id-1": "content-1" } }, + { "id": "-42", "ext": { "prebid": -42 } }, + { "id": "-1", "ext": { "bidder": {} } }, + { + "id": "-0", + "native": { "request": "" }, + "ext": { "bidder": { "owner": "test", "code": 0 } } + }, + { + "id": "42", + "native": { "request": "" }, + "ext": { + "bidder": { + "owner": "msq_test", + "code": "publishername_atf_desktop_rg_pave" + } + } + } + ], + "test": 1 + }, + "expectedMakeRequestsErrors": [ + { + "value": " imp[ext]: is empty.", + "comparison": "literal" + }, + { + "value": " imp-bidder[ext]: is empty.", + "comparison": "literal" + }, + { + "value": " imp[ext]: cannot unmarshal adapters.ExtImpBidder.Prebid: expect { or n, but found -", + "comparison": "literal" + }, + { + "value": " imp-bidder[ext]: cannot unmarshal openrtb_ext.ImpExtMediasquare.Code: expects \" or n, but found 0", + "comparison": "literal" + } + ], + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": null, + "gdpr": { "consent_required": false, "consent_string": "" }, + "type": "pbs", + "dsa": "", + "tech": { "device": null, "app": null }, + "test": true + }, + "impIDs": ["0", "1", "-42", "-1", "-0", "42"] + }, + + "mockResponse": { + "status": 200, + "body": { + "id": "id-ok", + "seatbid": [ + { + "seat": "mediasquare", + "bid": [ + { + "id": "42", + "impid": "1", + "price": 1.5, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320, + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": " Unexpected status code: 204. No responses found into body content.", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/supplemental/no-valid-response.json b/adapters/mediasquare/mediasquaretest/supplemental/no-valid-response.json new file mode 100644 index 00000000000..5e987db6155 --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/supplemental/no-valid-response.json @@ -0,0 +1,88 @@ +{ + "mockBidRequest": { + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "tagid": "msq_tag_200123_native", + "banner": null, + "native": null, + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + }, + "ext": null + } + ], + "app": null, + "device": null, + "regs": { + "gdpr": null, + "ext": { + "dsa": null + } + }, + "user": { + "ext": { + "consent": null + } + }, + "test": 1 + }, + "expectedMakeRequestsErrors": [ + { + "value": " imp-bidder[ext]: is empty.", + "comparison": "literal" + } + ], + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": null, + "gdpr": { + "consent_string": "", + "consent_required": false + }, + "type": "pbs", + "dsa": "", + "tech": { + "device": null, + "app": null + }, + "test": true + }, + "impIDs": ["2c35e25e-e7d3-41bf-b810-06a449f456b9"] + }, + + "mockResponse": { + "status": 200, + "body": { + "infos": { + "version": "42", + "description": "test-description", + "hostname": null + }, + "responses": "lol" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": " Unexpected status code: 406. Bad server response: cannot unmarshal mediasquare.msqResponse.Responses: decode slice: expect [ or n, but found \".", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/supplemental/status-400.json b/adapters/mediasquare/mediasquaretest/supplemental/status-400.json new file mode 100644 index 00000000000..05e4413a97d --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/supplemental/status-400.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "tagid": "msq_tag_200123_native", + "banner": null, + "native": null, + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_video" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": null + } + }, + "user": { + "ext": { + "consent": "consent-covering" + } + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "owner": "test", + "code": "publishername_atf_desktop_rg_video", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "mediatypes": { + "video": { + "ext": { + "linearity": 0, + "playersize": [[800, 600]], + "context": "context-test" + }, + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1 + } + }, + "floor": { + "*": { "floor": 1 }, + "800x600": { "floor": 1 } + } + } + ], + "gdpr": { + "consent_string": "consent-covering", + "consent_required": true + }, + "type": "pbs", + "dsa": "", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "test": true + }, + "impIDs": ["2c35e25e-e7d3-41bf-b810-06a449f456b9"] + }, + "mockResponse": { + "status": 400, + "body": null + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": " Unexpected status code: 400.", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediasquare/mediasquaretest/supplemental/status-not-200.json b/adapters/mediasquare/mediasquaretest/supplemental/status-not-200.json new file mode 100644 index 00000000000..4168a7adeaa --- /dev/null +++ b/adapters/mediasquare/mediasquaretest/supplemental/status-not-200.json @@ -0,0 +1,144 @@ +{ + "mockBidRequest": { + "id": "70e5672c-515b-406e-967c-fcc2b04de04f", + "imp": [ + { + "id": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "bidfloor": 1, + "tagid": "msq_tag_200123_native", + "banner": null, + "native": null, + "video": { + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1, + "ext": { + "context": "context-test", + "linearity": 0, + "playersize": [[800, 600]] + } + }, + "ext": { + "bidder": { + "owner": "test", + "code": "publishername_atf_desktop_rg_video" + } + } + } + ], + "app": { + "content": {}, + "domain": "debug.mediasquare.fr", + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + } + }, + "device": { + "devicetype": 1, + "geo": { + "country": "FRA", + "ipservice": 3 + }, + "ip": "92.154.6.0", + "language": "fr", + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" + }, + "regs": { + "gdpr": 1, + "ext": { + "dsa": null + } + }, + "user": { + "ext": { + "gdpr": "consent-covering" + } + }, + "test": 1 + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pbs-front.mediasquare.fr/msq_prebid", + "body": { + "codes": [ + { + "owner": "test", + "code": "publishername_atf_desktop_rg_video", + "adunit": "msq_tag_200123_native", + "auctionid": "70e5672c-515b-406e-967c-fcc2b04de04f", + "bidid": "2c35e25e-e7d3-41bf-b810-06a449f456b9", + "mediatypes": { + "video": { + "ext": { + "linearity": 0, + "context": "context-test", + "playersize": [[800, 600]] + }, + "mimes": ["video/mp4"], + "minduration": 10, + "maxduration": 23, + "placement": 1, + "w": 800, + "h": 600, + "plcmt": 1 + } + }, + "floor": { + "*": { "floor": 1 }, + "800x600": { "floor": 1 } + } + } + ], + "gdpr": { + "consent_string": "consent-covering", + "consent_required": true + }, + "type": "pbs", + "dsa": "", + "tech": { + "device": { + "geo": { + "ipservice": 3, + "country": "FRA" + }, + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "ip": "92.154.6.0", + "devicetype": 1, + "language": "fr" + }, + "app": { + "id": "app-id-test", + "name": "debug.mediasquare.fr", + "domain": "debug.mediasquare.fr", + "publisher": { + "id": "MEDIA_SQUARE" + }, + "content": {} + } + }, + "test": true + }, + "impIDs": ["2c35e25e-e7d3-41bf-b810-06a449f456b9"] + }, + + "mockResponse": { + "status": 42, + "body": null + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": " Unexpected status code: 42. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediasquare/params_test.go b/adapters/mediasquare/params_test.go new file mode 100644 index 00000000000..c9d2d5fe01e --- /dev/null +++ b/adapters/mediasquare/params_test.go @@ -0,0 +1,51 @@ +package mediasquare + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderMediasquare, json.RawMessage(p)); err != nil { + t.Errorf("Schema rejected valid params: %s", p) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderMediasquare, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{"owner":"owner-test", "code": "code-test"}`, +} + +var invalidParams = []string{ + `{"owner":"owner-test", "code": 42}`, + `{"owner":"owner-test", "code": nil}`, + `{"owner":"owner-test", "code": ""}`, + `{"owner": 42, "code": "code-test"}`, + `{"owner": nil, "code": "code-test"}`, + `{"owner": "", "code": "code-test"}`, + `nil`, + ``, + `[]`, + `true`, +} diff --git a/adapters/mediasquare/parsers.go b/adapters/mediasquare/parsers.go new file mode 100644 index 00000000000..f2ef4ab675d --- /dev/null +++ b/adapters/mediasquare/parsers.go @@ -0,0 +1,84 @@ +package mediasquare + +import ( + "fmt" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +// parserDSA: Struct used to extracts dsa content of a jsonutil. +type parserDSA struct { + DSA interface{} `json:"dsa,omitempty"` +} + +// setContent: Unmarshal a []byte into the parserDSA struct. +func (parser *parserDSA) setContent(extJsonBytes []byte) error { + if len(extJsonBytes) > 0 { + if err := jsonutil.Unmarshal(extJsonBytes, parser); err != nil { + return errorWriter(" extJsonBytes", err, false) + } + return nil + } + return errorWriter(" extJsonBytes", nil, true) +} + +// getValue: Returns the DSA value as a string, defaultly returns empty-string. +func (parser parserDSA) getValue(request *openrtb2.BidRequest) (dsa string) { + if request == nil || request.Regs == nil { + return + } + parser.setContent(request.Regs.Ext) + if parser.DSA != nil { + dsa = fmt.Sprint(parser.DSA) + } + return +} + +// parserGDPR: Struct used to extract pair of GDPR/Consent of a jsonutil. +type parserGDPR struct { + GDPR interface{} `json:"gdpr,omitempty"` + Consent interface{} `json:"consent,omitempty"` +} + +// setContent: Unmarshal a []byte into the parserGDPR struct. +func (parser *parserGDPR) setContent(extJsonBytes []byte) error { + if len(extJsonBytes) > 0 { + if err := jsonutil.Unmarshal(extJsonBytes, parser); err != nil { + return errorWriter(" extJsonBytes", err, false) + } + return nil + } + return errorWriter(" extJsonBytes", nil, true) +} + +// value: Returns the consent or GDPR-string depending of the parserGDPR content, defaulty return empty-string. +func (parser *parserGDPR) value() (gdpr string) { + switch { + case parser.Consent != nil: + gdpr = fmt.Sprint(parser.Consent) + case parser.GDPR != nil: + gdpr = fmt.Sprint(parser.GDPR) + } + return +} + +// getValue: Returns the consent or GDPR-string depending on the openrtb2.User content, defaultly returns empty-string. +func (parser parserGDPR) getValue(field string, request *openrtb2.BidRequest) (gdpr string) { + if request != nil { + switch { + case field == "consent_requirement" && request.Regs != nil: + gdpr = "false" + if ptrInt8ToBool(request.Regs.GDPR) { + gdpr = "true" + } + case field == "consent_string" && request.User != nil: + gdpr = request.User.Consent + if len(gdpr) <= 0 { + parser.setContent(request.User.Ext) + gdpr = parser.value() + } + } + } + return +} diff --git a/adapters/mediasquare/structs.go b/adapters/mediasquare/structs.go new file mode 100644 index 00000000000..3758e31380f --- /dev/null +++ b/adapters/mediasquare/structs.go @@ -0,0 +1,222 @@ +package mediasquare + +import ( + "fmt" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/util/ptrutil" +) + +// msqResponse: Bid-Response sent by mediasquare. +type msqResponse struct { + Infos struct { + Version string `json:"version"` + Description string `json:"description"` + Hostname string `json:"hostname,omitempty"` + } `json:"infos"` + Responses []msqResponseBids `json:"responses"` +} + +// msqParameters: Bid-Request sent to mediasquare. +type msqParameters struct { + Codes []msqParametersCodes `json:"codes"` + Gdpr msqParametersGdpr `json:"gdpr"` + Type string `json:"type"` + DSA interface{} `json:"dsa,omitempty"` + Support msqSupport `json:"tech"` + Test bool `json:"test"` +} + +type msqResponseBidsVideo struct { + Xml string `json:"xml"` + Url string `json:"url"` +} + +type nativeResponseImg struct { + Url string `json:"url"` + Width *int `json:"width,omitempty"` + Height *int `json:"height,omitempty"` +} + +type msqResponseBidsNative struct { + ClickUrl string `json:"clickUrl,omitempty"` + ClickTrackers []string `json:"clickTrackers,omitempty"` + ImpressionTrackers []string `json:"impressionTrackers,omitempty"` + JavascriptTrackers []string `json:"javascriptTrackers,omitempty"` + Privacy *string `json:"privacy,omitempty"` + Title *string `json:"title,omitempty"` + Icon *nativeResponseImg `json:"icon,omitempty"` + Image *nativeResponseImg `json:"image,omitempty"` + Cta *string `json:"cta,omitempty"` + Rating *string `json:"rating,omitempty"` + Downloads *string `json:"downloads,omitempty"` + Likes *string `json:"likes,omitempty"` + Price *string `json:"price,omitempty"` + SalePrice *string `json:"saleprice,omitempty"` + Address *string `json:"address,omitempty"` + Phone *string `json:"phone,omitempty"` + Body *string `json:"body,omitempty"` + Body2 *string `json:"body2,omitempty"` + SponsoredBy *string `json:"sponsoredBy,omitempty"` + DisplayUrl *string `json:"displayUrl,omitempty"` +} + +type msqResponseBids struct { + ID string `json:"id"` + Ad string `json:"ad,omitempty"` + BidId string `json:"bid_id,omitempty"` + Bidder string `json:"bidder,omitempty"` + Cpm float64 `json:"cpm,omitempty"` + Currency string `json:"currency,omitempty"` + CreativeId string `json:"creative_id,omitempty"` + Height int64 `json:"height,omitempty"` + Width int64 `json:"width,omitempty"` + NetRevenue bool `json:"net_revenue,omitempty"` + TransactionId string `json:"transaction_id,omitempty"` + Ttl int `json:"ttl,omitempty"` + Video *msqResponseBidsVideo `json:"video,omitempty"` + Native *msqResponseBidsNative `json:"native,omitempty"` + ADomain []string `json:"adomain,omitempty"` + Dsa interface{} `json:"dsa,omitempty"` + BURL string `json:"burl,omitempty"` +} + +type msqSupport struct { + Device interface{} `json:"device"` + App interface{} `json:"app"` +} + +type msqParametersCodes struct { + AdUnit string `json:"adunit"` + AuctionId string `json:"auctionid"` + BidId string `json:"bidid"` + Code string `json:"code"` + Owner string `json:"owner"` + Mediatypes mediaTypes `json:"mediatypes,omitempty"` + Floor map[string]msqFloor `json:"floor,omitempty"` +} + +type msqParametersGdpr struct { + ConsentRequired bool `json:"consent_required"` + ConsentString string `json:"consent_string"` +} + +type msqFloor struct { + Price float64 `json:"floor,omitempty"` + Currency string `json:"currency,omitempty"` +} + +type mediaTypes struct { + Banner *mediaTypeBanner `json:"banner,omitempty"` + Video *openrtb2.Video `json:"video,omitempty"` + NativeRequest *string `json:"native_request,omitempty"` +} + +type mediaTypeBanner struct { + Sizes [][]*int `json:"sizes"` +} + +func initMsqParams(request *openrtb2.BidRequest) (msqParams msqParameters) { + msqParams.Type = "pbs" + msqParams.Support = msqSupport{ + Device: request.Device, + App: request.App, + } + msqParams.Gdpr = msqParametersGdpr{ + ConsentRequired: (parserGDPR{}).getValue("consent_requirement", request) == "true", + ConsentString: (parserGDPR{}).getValue("consent_string", request), + } + msqParams.DSA = (parserDSA{}).getValue(request) + + return +} + +// setContent: Loads currentImp into msqParams (*msqParametersCodes), +// returns (ok bool) where `ok` express if mandatory content had been loaded. +func (msqParams *msqParametersCodes) setContent(currentImp openrtb2.Imp) (ok bool) { + var ( + currentMapFloors = make(map[string]msqFloor, 0) + currentFloor = msqFloor{ + Price: currentImp.BidFloor, + Currency: currentImp.BidFloorCur, + } + ) + + if currentImp.Video != nil { + ok = true + msqParams.Mediatypes.Video = currentImp.Video + if currentImp.Video.W != nil && currentImp.Video.H != nil { + currentMapFloors[fmt.Sprintf("%dx%d", *(currentImp.Video.W), *(currentImp.Video.H))] = currentFloor + } + currentMapFloors["*"] = currentFloor + } + + if currentImp.Banner != nil { + switch { + case len(currentImp.Banner.Format) > 0: + ok = true + msqParams.Mediatypes.Banner = new(mediaTypeBanner) + for _, bannerFormat := range currentImp.Banner.Format { + currentMapFloors[fmt.Sprintf("%dx%d", bannerFormat.W, bannerFormat.H)] = currentFloor + msqParams.Mediatypes.Banner.Sizes = append(msqParams.Mediatypes.Banner.Sizes, + []*int{ptrutil.ToPtr(int(bannerFormat.W)), ptrutil.ToPtr(int(bannerFormat.H))}) + } + case currentImp.Banner.W != nil && currentImp.Banner.H != nil: + ok = true + msqParams.Mediatypes.Banner = new(mediaTypeBanner) + currentMapFloors[fmt.Sprintf("%dx%d", *(currentImp.Banner.W), *(currentImp.Banner.H))] = currentFloor + msqParams.Mediatypes.Banner.Sizes = append(msqParams.Mediatypes.Banner.Sizes, + []*int{ptrutil.ToPtr(int(*currentImp.Banner.W)), ptrutil.ToPtr(int(*currentImp.Banner.H))}) + } + + if msqParams.Mediatypes.Banner != nil { + for _, bannerSizes := range msqParams.Mediatypes.Banner.Sizes { + if len(bannerSizes) == 2 && bannerSizes[0] != nil && bannerSizes[1] != nil { + currentMapFloors[fmt.Sprintf("%dx%d", *(bannerSizes[0]), *(bannerSizes[1]))] = currentFloor + } + } + } + } + + if currentImp.Native != nil && len(currentImp.Native.Request) > 0 { + ok = true + msqParams.Mediatypes.NativeRequest = ptrutil.ToPtr(currentImp.Native.Request) + currentMapFloors["*"] = currentFloor + } + + if len(currentMapFloors) > 0 { + msqParams.Floor = currentMapFloors + } + return +} + +// getContent: Loads msqResp content into the bidderResponse (*adapters.BidderResponse). +func (msqResp *msqResponse) getContent(bidderResponse *adapters.BidderResponse) { + var tmpBids []*adapters.TypedBid + for _, resp := range msqResp.Responses { + tmpTBid := adapters.TypedBid{ + BidType: resp.bidType(), + Bid: &openrtb2.Bid{ + ID: resp.ID, + ImpID: resp.BidId, + Price: resp.Cpm, + AdM: resp.Ad, + ADomain: resp.ADomain, + W: resp.Width, + H: resp.Height, + CrID: resp.CreativeId, + MType: resp.mType(), + BURL: resp.BURL, + Ext: resp.extBid(), + }, + BidMeta: resp.extBidPrebidMeta(), + } + tmpBids = append(tmpBids, &tmpTBid) + bidderResponse.Currency = resp.Currency + } + + if len(tmpBids) > 0 { + bidderResponse.Bids = tmpBids + } +} diff --git a/adapters/mediasquare/utils.go b/adapters/mediasquare/utils.go new file mode 100644 index 00000000000..50ee7285a7f --- /dev/null +++ b/adapters/mediasquare/utils.go @@ -0,0 +1,95 @@ +package mediasquare + +import ( + "encoding/json" + "fmt" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +var headerList = map[string][]string{ + "Content-Type": {"application/json;charset=utf-8"}, + "Accept": {"application/json"}, +} + +// mType: Returns the openrtb2.MarkupType from an msqResponseBids. +func (msqBids *msqResponseBids) mType() openrtb2.MarkupType { + switch { + case msqBids.Video != nil: + return openrtb2.MarkupVideo + case msqBids.Native != nil: + return openrtb2.MarkupNative + default: + return openrtb2.MarkupBanner + } +} + +// bidType: Returns the openrtb_ext.BidType from an msqResponseBids. +func (msqBids *msqResponseBids) bidType() openrtb_ext.BidType { + switch { + case msqBids.Video != nil: + return "video" + case msqBids.Native != nil: + return "native" + default: + return "banner" + } +} + +// extBid: Extracts the ExtBid from msqBids formated as (json.RawMessage). +func (msqBids *msqResponseBids) extBid() (raw json.RawMessage) { + extBid, _ := msqBids.loadExtBid() + if extBid.DSA != nil || extBid.Prebid != nil { + if bb, _ := jsonutil.Marshal(extBid); len(bb) > 0 { + raw = json.RawMessage(bb) + } + } + return +} + +// loadExtBid: Extracts the ExtBid from msqBids as (openrtb_ext.ExtBid, []error). +func (msqBids *msqResponseBids) loadExtBid() (extBid openrtb_ext.ExtBid, errs []error) { + if msqBids.Dsa != nil { + bb, err := jsonutil.Marshal(msqBids.Dsa) + if err != nil { + errs = append(errs, err) + } + if len(bb) > 0 { + var dsa openrtb_ext.ExtBidDSA + if err = jsonutil.Unmarshal(bb, &dsa); err != nil { + errs = append(errs, err) + } else { + extBid.DSA = &dsa + } + } + } + return +} + +// extBidPrebidMeta: Extracts the ExtBidPrebidMeta from msqBids as (*openrtb_ext.ExtBidPrebidMeta). +func (msqBids *msqResponseBids) extBidPrebidMeta() *openrtb_ext.ExtBidPrebidMeta { + var extBidMeta openrtb_ext.ExtBidPrebidMeta + if msqBids.ADomain != nil { + extBidMeta.AdvertiserDomains = msqBids.ADomain + } + extBidMeta.MediaType = string(msqBids.bidType()) + return &extBidMeta +} + +// ptrInt8ToBool: Returns (TRUE) when i equals 1. +func ptrInt8ToBool(i *int8) bool { + if i != nil { + return (*i == int8(1)) + } + return false +} + +// errorWriter: Returns a Custom error message. +func errorWriter(referer string, err error, isEmpty bool) error { + if isEmpty { + return fmt.Errorf("%s: is empty.", referer) + } + return fmt.Errorf("%s: %s", referer, err.Error()) +} diff --git a/adapters/oms/oms.go b/adapters/oms/oms.go index 201162b5699..3ea5bd3284e 100644 --- a/adapters/oms/oms.go +++ b/adapters/oms/oms.go @@ -31,9 +31,27 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte return nil, []error{err} } + var uri string = a.endpoint + if len(request.Imp[0].Ext) > 0 { + var bidderExt adapters.ExtImpBidder + if err := jsonutil.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil { + return nil, []error{err} + } + + var omsImpExt openrtb_ext.ExtImpOms + if err := jsonutil.Unmarshal(bidderExt.Bidder, &omsImpExt); err != nil { + return nil, []error{err} + } + + uri = fmt.Sprintf("%s?publisherId=%s", a.endpoint, omsImpExt.Pid) + if omsImpExt.Pid == "" && omsImpExt.PublisherID > 0 { + uri = fmt.Sprintf("%s?publisherId=%d", a.endpoint, omsImpExt.PublisherID) + } + } + requestData := &adapters.RequestData{ Method: "POST", - Uri: a.endpoint, + Uri: uri, Body: requestJSON, ImpIDs: openrtb_ext.GetImpIDs(request.Imp), } @@ -42,7 +60,6 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte } func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { - if responseData.StatusCode == http.StatusNoContent { return nil, nil } @@ -70,14 +87,43 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R if len(response.Cur) == 0 { bidResponse.Currency = response.Cur } + for _, seatBid := range response.SeatBid { for i := range seatBid.Bid { + bidType := getBidType(seatBid.Bid[i].MType) + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &seatBid.Bid[i], - BidType: openrtb_ext.BidTypeBanner, + Bid: &seatBid.Bid[i], + BidType: bidType, + BidVideo: getBidVideo(bidType, &seatBid.Bid[i]), }) } } return bidResponse, nil } + +func getBidType(markupType openrtb2.MarkupType) openrtb_ext.BidType { + switch markupType { + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo + default: + return openrtb_ext.BidTypeBanner + } +} + +func getBidVideo(bidType openrtb_ext.BidType, bid *openrtb2.Bid) *openrtb_ext.ExtBidPrebidVideo { + if bidType != openrtb_ext.BidTypeVideo { + return nil + } + + var primaryCategory string + if len(bid.Cat) > 0 { + primaryCategory = bid.Cat[0] + } + + return &openrtb_ext.ExtBidPrebidVideo{ + Duration: int(bid.Dur), + PrimaryCategory: primaryCategory, + } +} diff --git a/adapters/oms/omstest/exemplary/simple-banner-cookie-uid.json b/adapters/oms/omstest/exemplary/simple-banner-cookie-uid.json index c5f207dd46f..047697b48e2 100755 --- a/adapters/oms/omstest/exemplary/simple-banner-cookie-uid.json +++ b/adapters/oms/omstest/exemplary/simple-banner-cookie-uid.json @@ -35,7 +35,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/exemplary/simple-banner-multiple-bids.json b/adapters/oms/omstest/exemplary/simple-banner-multiple-bids.json index 7100809d198..ce131fa4492 100644 --- a/adapters/oms/omstest/exemplary/simple-banner-multiple-bids.json +++ b/adapters/oms/omstest/exemplary/simple-banner-multiple-bids.json @@ -62,7 +62,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id-multiple-bids", diff --git a/adapters/oms/omstest/exemplary/simple-banner-uid-with-publisher-id.json b/adapters/oms/omstest/exemplary/simple-banner-uid-with-publisher-id.json new file mode 100755 index 00000000000..52e49612ef6 --- /dev/null +++ b/adapters/oms/omstest/exemplary/simple-banner-uid-with-publisher-id.json @@ -0,0 +1,150 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "oms.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "oms.com", + "uids": [ + { + "id": "oms-eid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", + "headers": {}, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "oms.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "oms.com", + "uids": [ + { + "id": "oms-eid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisherId": 12345 + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} + diff --git a/adapters/oms/omstest/exemplary/simple-banner-uid.json b/adapters/oms/omstest/exemplary/simple-banner-uid.json index 678997d3e6c..1d748d6af16 100755 --- a/adapters/oms/omstest/exemplary/simple-banner-uid.json +++ b/adapters/oms/omstest/exemplary/simple-banner-uid.json @@ -46,7 +46,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/exemplary/simple-multi-type-banner.json b/adapters/oms/omstest/exemplary/simple-multi-type-banner.json index 5e0a0c9e5d2..c6916a52a92 100644 --- a/adapters/oms/omstest/exemplary/simple-multi-type-banner.json +++ b/adapters/oms/omstest/exemplary/simple-multi-type-banner.json @@ -52,7 +52,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/exemplary/simple-video.json b/adapters/oms/omstest/exemplary/simple-video.json new file mode 100644 index 00000000000..619c0ec48cb --- /dev/null +++ b/adapters/oms/omstest/exemplary/simple-video.json @@ -0,0 +1,165 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "oms.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "oms.com", + "uids": [ + { + "id": "oms-eid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-video-imp-id", + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ] + }, + "ext": { + "bidder": { + "pid": "12345" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", + "headers": {}, + "body": { + "id": "test-request-id", + "site": { + "id": "site-id", + "page": "oms.com" + }, + "device": { + "os": "android", + "ip": "91.199.242.236", + "ua": "random user agent" + }, + "user": { + "ext": { + "eids": [ + { + "source": "oms.com", + "uids": [ + { + "id": "oms-eid" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-video-imp-id", + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ] + }, + "ext": { + "bidder": { + "pid": "12345" + } + } + } + ] + }, + "impIDs": [ + "test-video-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "currency": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "test-slot-id", + "impid": "test-video-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 640, + "h": 480, + "dur": 10, + "cat": [ + "IAB20" + ], + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-slot-id", + "impid": "test-video-imp-id", + "price": 0.1, + "crid": "creative-123", + "adm": "", + "w": 640, + "h": 480, + "dur": 10, + "cat": [ + "IAB20" + ], + "mtype": 2 + }, + "video": { + "duration": 10, + "primary_category": "IAB20" + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/oms/omstest/supplemental/204-response-from-target.json b/adapters/oms/omstest/supplemental/204-response-from-target.json index f6a6edd034b..cd6c0a30e60 100755 --- a/adapters/oms/omstest/supplemental/204-response-from-target.json +++ b/adapters/oms/omstest/supplemental/204-response-from-target.json @@ -37,7 +37,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/supplemental/400-response-from-target.json b/adapters/oms/omstest/supplemental/400-response-from-target.json index 0151f98f208..3cf5c3eac15 100755 --- a/adapters/oms/omstest/supplemental/400-response-from-target.json +++ b/adapters/oms/omstest/supplemental/400-response-from-target.json @@ -37,7 +37,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/supplemental/500-response-from-target.json b/adapters/oms/omstest/supplemental/500-response-from-target.json index 8893e436f5f..cfc64e7becb 100755 --- a/adapters/oms/omstest/supplemental/500-response-from-target.json +++ b/adapters/oms/omstest/supplemental/500-response-from-target.json @@ -37,7 +37,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/omstest/supplemental/ext-unmarshal-error.json b/adapters/oms/omstest/supplemental/ext-unmarshal-error.json new file mode 100644 index 00000000000..f249f9aac86 --- /dev/null +++ b/adapters/oms/omstest/supplemental/ext-unmarshal-error.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "test-request-id-unmarshal", + "app": { + "bundle": "test.app.bundle" + }, + "device": { + "ifa": "test-ifa-123456", + "ip": "91.199.242.236", + "ua": "random user agent", + "os": "android" + }, + "regs": { + "ext": { + "us_privacy": "1YYY" + } + }, + "imp": [ + { + "id": "test-imp-id-unmarshal", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": "ext" + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/oms/omstest/supplemental/simple-banner-with-ipv6.json b/adapters/oms/omstest/supplemental/simple-banner-with-ipv6.json index 093ffeed648..59b87d43466 100755 --- a/adapters/oms/omstest/supplemental/simple-banner-with-ipv6.json +++ b/adapters/oms/omstest/supplemental/simple-banner-with-ipv6.json @@ -42,7 +42,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://rt.marphezis.com/pbs", + "uri": "http://rt.marphezis.com/pbs?publisherId=12345", "headers": {}, "body": { "id": "test-request-id", diff --git a/adapters/oms/params_test.go b/adapters/oms/params_test.go index 00b92699455..ed4113d4238 100644 --- a/adapters/oms/params_test.go +++ b/adapters/oms/params_test.go @@ -42,6 +42,10 @@ func TestInvalidParams(t *testing.T) { var validParams = []string{ `{"pid": "12345"}`, `{"pid": "123456"}`, + `{"publisherId": 12345}`, + `{"publisherId": 123456}`, + `{"publisherId": 12345,"pid": "12345"}`, + `{"publisherId": 10000}`, } var invalidParams = []string{ @@ -53,4 +57,6 @@ var invalidParams = []string{ `[]`, `{}`, `{"pid": "0"}`, + `{"publisherId": 9999}`, + `{"publisherId": "99999"}`, } diff --git a/adapters/rubicon/rubicon.go b/adapters/rubicon/rubicon.go index 5eddc83220c..91fac7a43f8 100644 --- a/adapters/rubicon/rubicon.go +++ b/adapters/rubicon/rubicon.go @@ -32,10 +32,6 @@ type RubiconAdapter struct { XAPIPassword string } -type rubiconContext struct { - Data json.RawMessage `json:"data"` -} - type rubiconData struct { AdServer rubiconAdServer `json:"adserver"` PbAdSlot string `json:"pbadslot"` @@ -47,13 +43,12 @@ type rubiconAdServer struct { } type rubiconExtImpBidder struct { - Prebid *openrtb_ext.ExtImpPrebid `json:"prebid"` - Bidder openrtb_ext.ExtImpRubicon `json:"bidder"` - Gpid string `json:"gpid"` - Skadn json.RawMessage `json:"skadn,omitempty"` - Tid string `json:"tid"` - Data json.RawMessage `json:"data"` - Context rubiconContext `json:"context"` + Prebid *openrtb_ext.ExtImpPrebid `json:"prebid"` + Bidder openrtb_ext.ExtImpRubicon `json:"bidder"` + Gpid string `json:"gpid"` + Skadn json.RawMessage `json:"skadn,omitempty"` + Tid string `json:"tid"` + Data json.RawMessage `json:"data"` } type bidRequestExt struct { @@ -439,37 +434,26 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *ada rubiconRequest.App = &appCopy } - if request.Source != nil || rubiconExt.PChain != "" { - var sourceCopy openrtb2.Source - if request.Source != nil { - sourceCopy = *request.Source - } else { - sourceCopy = openrtb2.Source{} - } - - if sourceCopy.SChain != nil { - var sourceCopyExt openrtb_ext.ExtSource - if sourceCopy.Ext != nil { - if err = jsonutil.Unmarshal(sourceCopy.Ext, &sourceCopyExt); err != nil { - errs = append(errs, &errortypes.BadInput{Message: err.Error()}) - continue - } - } else { - sourceCopyExt = openrtb_ext.ExtSource{} - } + if request.Source != nil && request.Source.SChain != nil { + sourceCopy := *request.Source - sourceCopyExt.SChain = sourceCopy.SChain - sourceCopy.SChain = nil - - sourceCopy.Ext, err = json.Marshal(&sourceCopyExt) - if err != nil { - errs = append(errs, err) + var sourceCopyExt openrtb_ext.ExtSource + if sourceCopy.Ext != nil { + if err = jsonutil.Unmarshal(sourceCopy.Ext, &sourceCopyExt); err != nil { + errs = append(errs, &errortypes.BadInput{Message: err.Error()}) continue } + } else { + sourceCopyExt = openrtb_ext.ExtSource{} } - if rubiconExt.PChain != "" { - sourceCopy.PChain = rubiconExt.PChain + sourceCopyExt.SChain = sourceCopy.SChain + sourceCopy.SChain = nil + + sourceCopy.Ext, err = json.Marshal(&sourceCopyExt) + if err != nil { + errs = append(errs, err) + continue } rubiconRequest.Source = &sourceCopy @@ -663,9 +647,7 @@ func (a *RubiconAdapter) updateImpRpTarget(extImp rubiconExtImpBidder, extImpRub } } - if len(extImp.Context.Data) > 0 { - err = populateFirstPartyDataAttributes(extImp.Context.Data, target) - } else if len(extImp.Data) > 0 { + if len(extImp.Data) > 0 { err = populateFirstPartyDataAttributes(extImp.Data, target) } if isNotKeyPathError(err) { @@ -679,18 +661,11 @@ func (a *RubiconAdapter) updateImpRpTarget(extImp rubiconExtImpBidder, extImpRub return nil, err } } - var contextData rubiconData - if len(extImp.Context.Data) > 0 { - err := jsonutil.Unmarshal(extImp.Context.Data, &contextData) - if err != nil { - return nil, err - } - } if data.PbAdSlot != "" { target["pbadslot"] = data.PbAdSlot } else { - dfpAdUnitCode := extractDfpAdUnitCode(data, contextData) + dfpAdUnitCode := extractDfpAdUnitCode(data) if dfpAdUnitCode != "" { target["dfp_ad_unit_code"] = dfpAdUnitCode } @@ -711,10 +686,8 @@ func (a *RubiconAdapter) updateImpRpTarget(extImp rubiconExtImpBidder, extImpRub return updatedTarget, nil } -func extractDfpAdUnitCode(data rubiconData, contextData rubiconData) string { - if contextData.AdServer.Name == "gam" && contextData.AdServer.AdSlot != "" { - return contextData.AdServer.AdSlot - } else if data.AdServer.Name == "gam" && data.AdServer.AdSlot != "" { +func extractDfpAdUnitCode(data rubiconData) string { + if data.AdServer.Name == "gam" && len(data.AdServer.AdSlot) != 0 { return data.AdServer.AdSlot } diff --git a/adapters/rubicon/rubicon_test.go b/adapters/rubicon/rubicon_test.go index 06a603b9d71..7ab62370ebb 100644 --- a/adapters/rubicon/rubicon_test.go +++ b/adapters/rubicon/rubicon_test.go @@ -479,12 +479,10 @@ func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) { "inventory": {"key1" : "val1"}, "visitor": {"key2" : "val2"} }, - "context": { - "data": { - "adserver": { - "adslot": "/test-adslot", - "name": "gam" - } + "data": { + "adserver": { + "adslot": "/test-adslot", + "name": "gam" } } }`), diff --git a/adapters/rubicon/rubicontest/exemplary/25-26-transition-period.json b/adapters/rubicon/rubicontest/exemplary/25-26-transition-period.json index 52cad5919eb..63eb23e5ff3 100644 --- a/adapters/rubicon/rubicontest/exemplary/25-26-transition-period.json +++ b/adapters/rubicon/rubicontest/exemplary/25-26-transition-period.json @@ -154,8 +154,7 @@ "bidder": { "accountId": 1001, "siteId": 113932, - "zoneId": 535510, - "pchain": "pchain" + "zoneId": 535510 }, "skadn": { "version": "1.0", @@ -323,7 +322,6 @@ "bundle": "com.wls.testwlsapplication" }, "source": { - "pchain": "pchain", "ext": { "schain": { "complete": 0, diff --git a/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json b/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json index 8982f95bff5..c90e463bab1 100644 --- a/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json +++ b/adapters/rubicon/rubicontest/exemplary/app-imp-fpd.json @@ -141,21 +141,13 @@ "h": 576 }, "ext": { - "context": { - "data": { - "adserver": { - "name": "gam", - "adslot": "adSlotFromContextData" - }, - "dataAttr1": "dataVal1", - "dataAttr2": "dataVal2" - } - }, "data": { "adserver": { "name": "gam", "adslot": "adSlotFromData" - } + }, + "dataAttr1": "dataVal1", + "dataAttr2": "dataVal2" }, "bidder": { "video": { @@ -315,7 +307,7 @@ "ext": { "rp": { "target": { - "dfp_ad_unit_code": "adSlotFromContextData", + "dfp_ad_unit_code": "adSlotFromData", "dataAttr1": [ "dataVal1" ], @@ -356,7 +348,9 @@ } ] }, - "impIDs":["test-imp-id"] + "impIDs": [ + "test-imp-id" + ] }, "mockResponse": { "status": 200, diff --git a/adapters/rubicon/rubicontest/exemplary/flexible-schema.json b/adapters/rubicon/rubicontest/exemplary/flexible-schema.json index 54399b0adf0..800231ae53f 100644 --- a/adapters/rubicon/rubicontest/exemplary/flexible-schema.json +++ b/adapters/rubicon/rubicontest/exemplary/flexible-schema.json @@ -141,21 +141,13 @@ "h": 576 }, "ext": { - "context": { - "data": { - "adserver": { - "name": "gam", - "adslot": "adSlotFromContextData" - }, - "dataAttr1": "dataVal1", - "dataAttr2": "dataVal2" - } - }, "data": { "adserver": { "name": "gam", "adslot": "adSlotFromData" - } + }, + "dataAttr1": "dataVal1", + "dataAttr2": "dataVal2" }, "bidder": { "video": { @@ -316,7 +308,7 @@ "ext": { "rp": { "target": { - "dfp_ad_unit_code": "adSlotFromContextData", + "dfp_ad_unit_code": "adSlotFromData", "dataAttr1": [ "dataVal1" ], @@ -358,7 +350,9 @@ } ] }, - "impIDs":["test-imp-id"] + "impIDs": [ + "test-imp-id" + ] }, "mockResponse": { "status": 200, diff --git a/adapters/rubicon/rubicontest/exemplary/simple-banner.json b/adapters/rubicon/rubicontest/exemplary/simple-banner.json index 86663034adb..2f095ab5b3c 100644 --- a/adapters/rubicon/rubicontest/exemplary/simple-banner.json +++ b/adapters/rubicon/rubicontest/exemplary/simple-banner.json @@ -131,8 +131,7 @@ "bidder": { "accountId": 1001, "siteId": 113932, - "zoneId": 535510, - "pchain": "pchain" + "zoneId": 535510 }, "skadn": { "version": "1.0", @@ -299,9 +298,6 @@ "id": "1", "bundle": "com.wls.testwlsapplication" }, - "source": { - "pchain": "pchain" - }, "imp": [ { "id": "test-imp-id", diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 94d281e25e6..f9eaa9c198f 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -144,6 +144,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/marsmedia" "github.com/prebid/prebid-server/v3/adapters/mediago" "github.com/prebid/prebid-server/v3/adapters/medianet" + "github.com/prebid/prebid-server/v3/adapters/mediasquare" "github.com/prebid/prebid-server/v3/adapters/melozen" "github.com/prebid/prebid-server/v3/adapters/metax" "github.com/prebid/prebid-server/v3/adapters/mgid" @@ -393,6 +394,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderMediafuse: appnexus.Builder, openrtb_ext.BidderMediaGo: mediago.Builder, openrtb_ext.BidderMedianet: medianet.Builder, + openrtb_ext.BidderMediasquare: mediasquare.Builder, openrtb_ext.BidderMeloZen: melozen.Builder, openrtb_ext.BidderMetaX: metax.Builder, openrtb_ext.BidderMgid: mgid.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 5225ff5a20e..b5ba5a7060f 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -162,6 +162,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderMediafuse, BidderMediaGo, BidderMedianet, + BidderMediasquare, BidderMeloZen, BidderMetaX, BidderMgid, @@ -515,6 +516,7 @@ const ( BidderMediafuse BidderName = "mediafuse" BidderMediaGo BidderName = "mediago" BidderMedianet BidderName = "medianet" + BidderMediasquare BidderName = "mediasquare" BidderMeloZen BidderName = "melozen" BidderMetaX BidderName = "metax" BidderMgid BidderName = "mgid" diff --git a/openrtb_ext/imp_mediasquare.go b/openrtb_ext/imp_mediasquare.go new file mode 100644 index 00000000000..8f1291e6086 --- /dev/null +++ b/openrtb_ext/imp_mediasquare.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ImpExtMediasquare struct { + Owner string `json:"owner"` + Code string `json:"code"` +} diff --git a/openrtb_ext/imp_oms.go b/openrtb_ext/imp_oms.go index 4e12da86264..8defe1da85f 100644 --- a/openrtb_ext/imp_oms.go +++ b/openrtb_ext/imp_oms.go @@ -2,5 +2,6 @@ package openrtb_ext // ExtImpOms defines the contract for bidrequest.imp[i].ext.prebid.bidder.oms type ExtImpOms struct { - PublisherID string `json:"pid"` + Pid string `json:"pid"` + PublisherID int `json:"publisherId"` } diff --git a/openrtb_ext/imp_rubicon.go b/openrtb_ext/imp_rubicon.go index 7264be1374c..5f76aed9918 100644 --- a/openrtb_ext/imp_rubicon.go +++ b/openrtb_ext/imp_rubicon.go @@ -15,7 +15,6 @@ type ExtImpRubicon struct { Visitor json.RawMessage `json:"visitor,omitempty"` Video rubiconVideoParams `json:"video"` Debug impExtRubiconDebug `json:"debug,omitempty"` - PChain string `json:"pchain,omitempty"` } // rubiconVideoParams defines the contract for bidrequest.imp[i].ext.prebid.bidder.rubicon.video diff --git a/static/bidder-info/mediasquare.yaml b/static/bidder-info/mediasquare.yaml new file mode 100644 index 00000000000..b6d21f4c9f2 --- /dev/null +++ b/static/bidder-info/mediasquare.yaml @@ -0,0 +1,12 @@ +endpoint: "https://pbs-front.mediasquare.fr/msq_prebid" +endpointCompression: gzip +gvlVendorID: 791 +modifyingVastXmlAllowed: true +maintainer: + email: "tech@mediasquare.fr" +capabilities: + app: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-info/oms.yaml b/static/bidder-info/oms.yaml index 8bb9299d6e9..c6d388e4e1f 100644 --- a/static/bidder-info/oms.yaml +++ b/static/bidder-info/oms.yaml @@ -5,7 +5,8 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner - + - video diff --git a/static/bidder-info/rtbhouse.yaml b/static/bidder-info/rtbhouse.yaml index fd9df062b15..f9dc83f5e3d 100644 --- a/static/bidder-info/rtbhouse.yaml +++ b/static/bidder-info/rtbhouse.yaml @@ -10,6 +10,8 @@ endpoint: "http://prebidserver-s2s-ams.creativecdn.com/bidder/prebidserver/bids" # endpoint: "http://prebidserver-s2s-sin.creativecdn.com/bidder/prebidserver/bids" geoscope: - global +openrtb: + version: 2.6 maintainer: email: "prebid@rtbhouse.com" endpointCompression: gzip diff --git a/static/bidder-info/undertone.yaml b/static/bidder-info/undertone.yaml index 0b9cc72b39b..bc7172170a0 100644 --- a/static/bidder-info/undertone.yaml +++ b/static/bidder-info/undertone.yaml @@ -3,6 +3,8 @@ maintainer: email: csop@undertone.com endpointCompression: gzip gvlVendorID: 677 +openrtb: + version: 2.6 modifyingVastXmlAllowed: true capabilities: app: diff --git a/static/bidder-params/mediasquare.json b/static/bidder-params/mediasquare.json new file mode 100644 index 00000000000..db7d07201f7 --- /dev/null +++ b/static/bidder-params/mediasquare.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Mediasquare Adapter Params", + "description": "A schema which validates params accepted by the Mediasquare adapter", + "type": "object", + "properties": { + "owner": { + "type": "string", + "minLength": 1, + "description": "The owner provided for mediasquare." + }, + "code": { + "type": "string", + "minLength": 1, + "description": "The code provided for mediasquare." + } + }, + "required": ["owner", "code"] +} diff --git a/static/bidder-params/oms.json b/static/bidder-params/oms.json index f33286d10d9..c53980698af 100644 --- a/static/bidder-params/oms.json +++ b/static/bidder-params/oms.json @@ -6,9 +6,25 @@ "properties": { "pid": { "type": "string", - "description": "An id used to identify OMS publisher.", + "description": "Deprecated: An id used to identify OMS publisher.", "minLength": 5 + }, + "publisherId": { + "type": "integer", + "description": "An ID used to identify OMS publisher.", + "minimum": 10000 } }, - "required": ["pid"] + "anyOf": [ + { + "required": [ + "pid" + ] + }, + { + "required": [ + "publisherId" + ] + } + ] }