diff --git a/adapters/mediago/mediago.go b/adapters/mediago/mediago.go new file mode 100644 index 00000000000..2ef46310bd0 --- /dev/null +++ b/adapters/mediago/mediago.go @@ -0,0 +1,210 @@ +package mediago + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +type adapter struct { + EndpointTemplate *template.Template +} + +type mediagoResponseBidExt struct { + MediaType string `json:"mediaType"` +} + +// Builder builds a new instance of the MediaGo adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + endpoint, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + bidder := &adapter{ + EndpointTemplate: endpoint, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var adapterRequests []*adapters.RequestData + var errs []error + + adapterRequest, err := a.makeRequest(request) + if err == nil { + adapterRequests = append(adapterRequests, adapterRequest) + } else { + errs = append(errs, err) + } + return adapterRequests, errs +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) { + + mediagoExt, err := getMediaGoExt(request) + if err != nil { + return nil, err + } + endPoint, err := a.getEndPoint(mediagoExt) + if err != nil { + return nil, err + } + + preProcess(request) + reqBody, err := json.Marshal(request) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + + return &adapters.RequestData{ + Method: "POST", + Uri: endPoint, + Body: reqBody, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }, nil +} + +// getMediaGoExt get MediaGoExt From ext.bidderparams or ext of First Imp. Only check and get first Imp.Ext.Bidder to ExtImpMediago +func getMediaGoExt(request *openrtb2.BidRequest) (*openrtb_ext.ExtMediaGo, error) { + var extMediaGo openrtb_ext.ExtMediaGo + var extBidder adapters.ExtImpBidder + + // first get the mediago ext from ext.bidderparams + reqExt := &openrtb_ext.ExtRequest{} + err := json.Unmarshal(request.Ext, &reqExt) + if err != nil { + err = json.Unmarshal(reqExt.Prebid.BidderParams, &extMediaGo) + if err != nil && extMediaGo.Token != "" { + return &extMediaGo, nil + } + } + + // fallback to get token and region from first imp + imp := request.Imp[0] + err = json.Unmarshal(imp.Ext, &extBidder) + if err != nil { + return nil, err + } + + var extImpMediaGo openrtb_ext.ExtImpMediaGo + err = json.Unmarshal(extBidder.Bidder, &extImpMediaGo) + if err != nil { + return nil, err + } + if extImpMediaGo.Token != "" { + extMediaGo.Token = extImpMediaGo.Token + extMediaGo.Region = extImpMediaGo.Region + + return &extMediaGo, nil + } + return nil, errors.New("mediago token not found") + +} + +func getRegionInfo(region string) string { + switch region { + case "APAC": + return "jp" + case "EU": + return "eu" + case "US": + return "us" + default: + return "us" + } +} + +func (a *adapter) getEndPoint(ext *openrtb_ext.ExtMediaGo) (string, error) { + endPointParams := macros.EndpointTemplateParams{ + AccountID: url.PathEscape(ext.Token), + Host: url.PathEscape(getRegionInfo(ext.Region)), + } + return macros.ResolveMacros(a.EndpointTemplate, endPointParams) +} + +func preProcess(request *openrtb2.BidRequest) { + for i := range request.Imp { + if request.Imp[i].Banner != nil { + banner := *request.Imp[i].Banner + if (banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0) && len(banner.Format) > 0 { + firstFormat := banner.Format[0] + banner.W = &firstFormat.W + banner.H = &firstFormat.H + request.Imp[i].Banner = &banner + } + } + } +} + +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 bidResp openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) + var errs []error + + for _, seatBid := range bidResp.SeatBid { + for idx := range seatBid.Bid { + mediaType, err := getBidType(seatBid.Bid[idx], internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &seatBid.Bid[idx], + BidType: mediaType, + }) + } + } + } + + return bidResponse, errs +} + +func getBidType(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + for _, imp := range imps { + if imp.ID == bid.ImpID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } + if imp.Native != nil { + return openrtb_ext.BidTypeNative, nil + } + } + } + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unsupported MType %d", bid.MType), + } + } + +} diff --git a/adapters/mediago/mediago_test.go b/adapters/mediago/mediago_test.go new file mode 100644 index 00000000000..2ac094045e5 --- /dev/null +++ b/adapters/mediago/mediago_test.go @@ -0,0 +1,28 @@ +package mediago + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderMediaGo, config.Adapter{ + Endpoint: "https://REGION.mediago.io/api/bid?tn={{.AccountID}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "mediagotest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderMediaGo, config.Adapter{Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-banner-apac.json b/adapters/mediago/mediagotest/exemplary/sample-banner-apac.json new file mode 100644 index 00000000000..79105827da3 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-banner-apac.json @@ -0,0 +1,114 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId", + "region": "APAC" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "APAC" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "APAC", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "APAC" + } + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-banner-euc.json b/adapters/mediago/mediagotest/exemplary/sample-banner-euc.json new file mode 100644 index 00000000000..98e37ab9912 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-banner-euc.json @@ -0,0 +1,114 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId", + "region": "EU" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "EU" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "EU", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "EU" + } + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-banner-fallback-to-first-imp-to-get-ep.json b/adapters/mediago/mediagotest/exemplary/sample-banner-fallback-to-first-imp-to-get-ep.json new file mode 100644 index 00000000000..b396be54bf6 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-banner-fallback-to-first-imp-to-get-ep.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId", + "region": "US" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-banner-use.json b/adapters/mediago/mediagotest/exemplary/sample-banner-use.json new file mode 100644 index 00000000000..b157b5a6a57 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-banner-use.json @@ -0,0 +1,114 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId", + "region": "US" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US" + } + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-banner.json b/adapters/mediago/mediagotest/exemplary/sample-banner.json new file mode 100644 index 00000000000..41effb8509b --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-banner.json @@ -0,0 +1,112 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "region": "US" + } + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid" + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-native.json b/adapters/mediago/mediagotest/exemplary/sample-native.json new file mode 100644 index 00000000000..eb302df2375 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-native.json @@ -0,0 +1,91 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid" + + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-nobid.json b/adapters/mediago/mediagotest/exemplary/sample-nobid.json new file mode 100644 index 00000000000..7c2d7e351b3 --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-nobid.json @@ -0,0 +1,62 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] + +} diff --git a/adapters/mediago/mediagotest/exemplary/sample-with-mtype.json b/adapters/mediago/mediagotest/exemplary/sample-with-mtype.json new file mode 100644 index 00000000000..609b916757e --- /dev/null +++ b/adapters/mediago/mediagotest/exemplary/sample-with-mtype.json @@ -0,0 +1,152 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + }, + { + "id":"test-imp-id2", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + }, + { + "id":"test-imp-id2", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + } + }, + "impIDs":["test-imp-id", "test-imp-id2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + }, { + "id": "test-imp-id2", + "impid": "test-imp-id2", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-imp-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "test-imp-id2", + "impid": "test-imp-id2", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_400_reponse.json b/adapters/mediago/mediagotest/supplemental/bad_400_reponse.json new file mode 100644 index 00000000000..c40f0db418b --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_400_reponse.json @@ -0,0 +1,65 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_500_reponse.json b/adapters/mediago/mediagotest/supplemental/bad_500_reponse.json new file mode 100644 index 00000000000..b653b33eaac --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_500_reponse.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 500 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_imp_ext.json b/adapters/mediago/mediagotest/supplemental/bad_imp_ext.json new file mode 100644 index 00000000000..191e4e33077 --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_imp_ext.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": "badExt" + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_impext_bidder.json b/adapters/mediago/mediagotest/supplemental/bad_impext_bidder.json new file mode 100644 index 00000000000..08e652b5cae --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_impext_bidder.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": "badBidder" + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpMediaGo", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_request_no_token.json b/adapters/mediago/mediagotest/supplemental/bad_request_no_token.json new file mode 100644 index 00000000000..51034b2eefd --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_request_no_token.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "placementId": "testPlacementId", + "region": "APAC" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "mediago token not found", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_response.json b/adapters/mediago/mediagotest/supplemental/bad_response.json new file mode 100644 index 00000000000..d72ea408d39 --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_response.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": "{\"id\":test-request-id" + } + } + ], + + "expectedMakeBidsErrors": [ + { + "comparison": "literal", + "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse" + } + ] +} diff --git a/adapters/mediago/mediagotest/supplemental/bad_response_error_mtype.json b/adapters/mediago/mediagotest/supplemental/bad_response_error_mtype.json new file mode 100644 index 00000000000..65167e04c5a --- /dev/null +++ b/adapters/mediago/mediagotest/supplemental/bad_response_error_mtype.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + }, + { + "id":"test-imp-id2", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://REGION.mediago.io/api/bid?tn=f9f2b1ef23fe2759c2cad0953029a94b", + "body": { + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}], + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + }, + { + "id":"test-imp-id2", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + ], + "site": { + "id": "test-site-id", + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "bidderparams": { + "bidder": { + "token": "f9f2b1ef23fe2759c2cad0953029a94b" + } + } + } + } + }, + "impIDs":["test-imp-id", "test-imp-id2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "mediago", + "bid": [{ + "id": "test-imp-id", + "price": 0.5, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 2 + }, { + "id": "test-imp-id2", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 5 + }] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [] + }], + "expectedMakeBidsErrors": [ + { + "value": "Unsupported MType 2", + "comparison": "literal" + }, + { + "value": "Unsupported MType 5", + "comparison": "literal" + } + ] +} diff --git a/adapters/mediago/params_test.go b/adapters/mediago/params_test.go new file mode 100644 index 00000000000..187248a45bf --- /dev/null +++ b/adapters/mediago/params_test.go @@ -0,0 +1,50 @@ +package mediago + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderMediaGo, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected mediago params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the mediago 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.BidderMediaGo, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"token": "f9f2b1ef23fe2759c2cad0953029a94b"}`, + `{"token": "f9f2b1ef23fe2759c2cad0953029a94b", "region": "APAC"}`, + `{"token": "f9f2b1ef23fe2759c2cad0953029a94b", "region": "US"}`, + `{"token": "f9f2b1ef23fe2759c2cad0953029a94b", "region": "EU"}`, +} + +var invalidParams = []string{ + `{}`, + `{"tn": "f9f2b1ef23fe2759c2cad0953029a94b"}`, + `{"region": "APAC"}`, + `{"region": "US"}`, + `{"tn": "f9f2b1ef23fe2759c2cad0953029a94b", "region": "EU"}`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index f4a2e8ec7fa..80c5910b1dc 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -123,6 +123,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/mabidder" "github.com/prebid/prebid-server/v2/adapters/madvertise" "github.com/prebid/prebid-server/v2/adapters/marsmedia" + "github.com/prebid/prebid-server/v2/adapters/mediago" "github.com/prebid/prebid-server/v2/adapters/medianet" "github.com/prebid/prebid-server/v2/adapters/mgid" "github.com/prebid/prebid-server/v2/adapters/mgidX" @@ -338,6 +339,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderMadvertise: madvertise.Builder, openrtb_ext.BidderMarsmedia: marsmedia.Builder, openrtb_ext.BidderMediafuse: appnexus.Builder, + openrtb_ext.BidderMediaGo: mediago.Builder, openrtb_ext.BidderMedianet: medianet.Builder, openrtb_ext.BidderMgid: mgid.Builder, openrtb_ext.BidderMgidX: mgidX.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 49f5d317ff8..a70a0964602 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -141,6 +141,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderMadvertise, BidderMarsmedia, BidderMediafuse, + BidderMediaGo, BidderMedianet, BidderMgid, BidderMgidX, @@ -453,6 +454,7 @@ const ( BidderMadvertise BidderName = "madvertise" BidderMarsmedia BidderName = "marsmedia" BidderMediafuse BidderName = "mediafuse" + BidderMediaGo BidderName = "mediago" BidderMedianet BidderName = "medianet" BidderMgid BidderName = "mgid" BidderMgidX BidderName = "mgidX" diff --git a/openrtb_ext/imp_mediago.go b/openrtb_ext/imp_mediago.go new file mode 100644 index 00000000000..89a5ff0dade --- /dev/null +++ b/openrtb_ext/imp_mediago.go @@ -0,0 +1,13 @@ +package openrtb_ext + +// ExtImpMediaGo defines the contract for bidrequest.imp[i].ext.prebid.bidder.mediago +type ExtImpMediaGo struct { + Token string `json:"token"` + Region string `json:"region"` + PlacementId string `json:"placementId"` +} + +type ExtMediaGo struct { + Token string `json:"token"` + Region string `json:"region"` +} diff --git a/static/bidder-info/mediago.yaml b/static/bidder-info/mediago.yaml new file mode 100644 index 00000000000..78b03e2595a --- /dev/null +++ b/static/bidder-info/mediago.yaml @@ -0,0 +1,26 @@ +# Contact ext_mediago_cm@baidu.com to ask about enabling a connection to the MediaGo DSP. +# We have the following regional endpoint domains: rtb-us, rtb-eu, rtb-jp +# Please deploy this config in each of your datacenters with the appropriate regional subdomain +endpoint: "https://REGION.mediago.io/api/bid?tn={{.AccountID}}" +endpointCompression: gzip +geoscope: + - USA + - DEU + - JPN + - GBR + - KOR + - CAN + - FRA + - ITA +maintainer: + email: ext_mediago_cm@baidu.com +gvlVendorID: 1020 +capabilities: + site: + mediaTypes: + - banner + - native +userSync: + redirect: + url: https://trace.mediago.io/ju/cs/prebid?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID \ No newline at end of file diff --git a/static/bidder-params/mediago.json b/static/bidder-params/mediago.json new file mode 100644 index 00000000000..ce482964d85 --- /dev/null +++ b/static/bidder-params/mediago.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "MediaGo Adapter Params", + "description": "A schema which validates params accepted by the MediaGo adapter", + "type": "object", + "properties": { + "token": { + "type": "string", + "description": "Publisher token,communicate with MediaGo to obtain it. This parameter expects all imps to be the same.", + "minLength": 1 + }, + "region": { + "type": "string", + "enum": ["US", "EU", "APAC"], + "description": "Server region for PBS request: US for US Region, EU for EU Region, APAC for APAC Region, default is US. This parameter expects all imps to be the same" + }, + "placementId": { + "type": "string", + "description": "The AD placement ID.", + "minLength": 1 + } + }, + "required": ["token"] +} \ No newline at end of file