diff --git a/adapters/adverxo/adverxo.go b/adapters/adverxo/adverxo.go new file mode 100644 index 0000000000..c76038ebde --- /dev/null +++ b/adapters/adverxo/adverxo.go @@ -0,0 +1,226 @@ +package adverxo + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "text/template" + + "github.com/buger/jsonparser" + "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/macros" + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +type adapter struct { + endpointTemplate *template.Template +} + +// Builder builds a new instance of the Adverxo adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + urlTemplate, 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: urlTemplate, + } + + return bidder, nil +} + +func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var ( + result []*adapters.RequestData + errors []error + ) + + for i := range request.Imp { + imp := request.Imp[i] + + adUnitParams, err := getAdUnitsParams(imp) + if err != nil { + errors = append(errors, err) + continue + } + + endpointUrl, err := adapter.buildEndpointURL(adUnitParams) + if err != nil { + errors = append(errors, err) + continue + } + + err = modifyImp(&imp, requestInfo) + if err != nil { + errors = append(errors, err) + continue + } + + thisRequest := makeRequestCopyWithImp(request, imp) + thisRequestBody, err := json.Marshal(thisRequest) + + if err != nil { + errors = append(errors, err) + continue + } + + result = append(result, &adapters.RequestData{ + Method: "POST", + Uri: endpointUrl, + Body: thisRequestBody, + ImpIDs: []string{imp.ID}, + }) + } + + return result, errors +} + +func (adapter *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode != http.StatusOK { + err := &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), + } + return nil, []error{err} + } + + var response openrtb2.BidResponse + if err := json.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + bidResponse.Currency = response.Cur + + for _, seatBid := range response.SeatBid { + for _, bid := range seatBid.Bid { + bid := bid + bidType, err := getMediaTypeForBid(&bid) + if err != nil { + return nil, []error{err} + } + + // for native bid responses fix Adm field + if bidType == openrtb_ext.BidTypeNative { + bid.AdM, err = getNativeAdm(bid.AdM) + if err != nil { + return nil, []error{err} + } + } + + resolveMacros(&bid) + + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } + } + + return bidResponse, nil +} + +func getAdUnitsParams(imp openrtb2.Imp) (*openrtb_ext.ImpExtAdverxo, error) { + var ext adapters.ExtImpBidder + + if err := json.Unmarshal(imp.Ext, &ext); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("imp %s: unable to unmarshal ext", imp.ID), + } + } + + var adverxoExt openrtb_ext.ImpExtAdverxo + if err := json.Unmarshal(ext.Bidder, &adverxoExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("imp %s: unable to unmarshal ext.bidder: %v", imp.ID, err), + } + } + + return &adverxoExt, nil +} + +func modifyImp(imp *openrtb2.Imp, requestInfo *adapters.ExtraRequestInfo) error { + if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" { + // Convert to US dollars + convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD") + if err != nil { + return err + } + + // Update after conversion. All imp elements inside request.Imp are shallow copies + // therefore, their non-pointer values are not shared memory and are safe to modify. + imp.BidFloorCur = "USD" + imp.BidFloor = convertedValue + } + + return nil +} + +func makeRequestCopyWithImp(request *openrtb2.BidRequest, imp openrtb2.Imp) openrtb2.BidRequest { + requestCopy := *request + requestCopy.Imp = []openrtb2.Imp{imp} + + return requestCopy +} + +func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ImpExtAdverxo) (string, error) { + endpointParams := macros.EndpointTemplateParams{ + AdUnit: strconv.Itoa(params.AdUnitId), + TokenID: params.Auth, + } + + return macros.ResolveMacros(adapter.endpointTemplate, endpointParams) +} + +func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + default: + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("unsupported MType %d", bid.MType), + } + } +} + +func getNativeAdm(adm string) (string, error) { + nativeAdm := make(map[string]interface{}) + err := json.Unmarshal([]byte(adm), &nativeAdm) + if err != nil { + return adm, errors.New("unable to unmarshal native adm") + } + + // move bid.adm.native to bid.adm + if _, ok := nativeAdm["native"]; ok { + //using jsonparser to avoid marshaling, encode escape, etc. + value, dataType, _, err := jsonparser.Get([]byte(adm), string(openrtb_ext.BidTypeNative)) + if err != nil || dataType != jsonparser.Object { + return adm, errors.New("unable to get native adm") + } + adm = string(value) + } + + return adm, nil +} + +func resolveMacros(bid *openrtb2.Bid) { + if bid != nil { + price := strconv.FormatFloat(bid.Price, 'f', -1, 64) + bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1) + } +} diff --git a/adapters/adverxo/adverxo_test.go b/adapters/adverxo/adverxo_test.go new file mode 100644 index 0000000000..ca32d50bc3 --- /dev/null +++ b/adapters/adverxo/adverxo_test.go @@ -0,0 +1,28 @@ +package adverxo + +import ( + "testing" + + "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 TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderAdverxo, config.Adapter{ + Endpoint: "https://example.com/auction?id={{.AdUnit}}&auth={{.TokenID}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "adverxotest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAdverxo, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/adverxo/adverxotest/exemplary/banner.json b/adapters/adverxo/adverxotest/exemplary/banner.json new file mode 100644 index 0000000000..8182e9f54a --- /dev/null +++ b/adapters/adverxo/adverxotest/exemplary/banner.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=520&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "content", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/exemplary/native.json b/adapters/adverxo/adverxotest/exemplary/native.json new file mode 100644 index 0000000000..325471c30c --- /dev/null +++ b/adapters/adverxo/adverxotest/exemplary/native.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 18, + "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=18&auth=fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 18, + "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "{\"native\":{\"assets\":[{\"id\":2,\"title\":{\"text\":\"Title\"}},{\"id\":3,\"data\":{\"value\":\"Description\"}},{\"id\":1,\"img\":{\"url\":\"http://example.com?img\",\"w\":150,\"h\":50}}],\"link\":{\"url\":\"http://example.com?link\"}}}", + "h": 600, + "w": 300, + "mtype": 4 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "{\"assets\":[{\"id\":2,\"title\":{\"text\":\"Title\"}},{\"id\":3,\"data\":{\"value\":\"Description\"}},{\"id\":1,\"img\":{\"url\":\"http://example.com?img\",\"w\":150,\"h\":50}}],\"link\":{\"url\":\"http://example.com?link\"}}", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/exemplary/video.json b/adapters/adverxo/adverxotest/exemplary/video.json new file mode 100644 index 0000000000..046eadc1cd --- /dev/null +++ b/adapters/adverxo/adverxotest/exemplary/video.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "testid", + "imp": [ + { + "id": "testimpid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "adUnitId": 3, + "auth": "qwerty" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=3&auth=qwerty", + "body":{ + "id": "testid", + "imp": [{ + "id": "testimpid", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "adUnitId": 3, + "auth": "qwerty" + } + } + }] + }, + "impIDs":["testimpid"] + }, + "mockResponse": { + "status": 200, + "body": { + "seatbid": [ + { + "bid": [ + { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048", + "mtype": 2 + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "crid": "24080", + "adid": "2068416", + "price": 0.01, + "id": "testid", + "impid": "testimpid", + "cid": "8048", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/bad_imp_ext.json b/adapters/adverxo/adverxotest/supplemental/bad_imp_ext.json new file mode 100644 index 0000000000..9c5e16c98e --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/bad_imp_ext.json @@ -0,0 +1,21 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": "badExt" + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "imp test-imp-id: unable to unmarshal ext", + "comparison": "literal" + } + ] +} diff --git a/adapters/adverxo/adverxotest/supplemental/bad_impext_bidder.json b/adapters/adverxo/adverxotest/supplemental/bad_impext_bidder.json new file mode 100644 index 0000000000..c61652f53e --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/bad_impext_bidder.json @@ -0,0 +1,23 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 320, "h": 50}] + }, + "ext": { + "bidder": "badBidder" + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "imp test-imp-id: unable to unmarshal ext.bidder: json: cannot unmarshal string into Go value of type openrtb_ext.ImpExtAdverxo", + "comparison": "literal" + } + ] +} diff --git a/adapters/adverxo/adverxotest/supplemental/currency_conversion.json b/adapters/adverxo/adverxotest/supplemental/currency_conversion.json new file mode 100644 index 0000000000..3562b05e7a --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/currency_conversion.json @@ -0,0 +1,138 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "bidfloor": 10.00, + "bidfloorcur": "MXN", + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "MXN": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=520&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "bidfloor": 0.50, + "bidfloorcur": "USD", + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "MXN": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "content", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/currency_rate_not_found.json b/adapters/adverxo/adverxotest/supplemental/currency_rate_not_found.json new file mode 100644 index 0000000000..9bc2ef75fd --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/currency_rate_not_found.json @@ -0,0 +1,44 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "bidfloor": 1.00, + "bidfloorcur": "JPY", + "ext":{ + "bidder": { + "adUnitId": 18, + "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44" + } + } + } + ], + "ext": { + "prebid": { + "currency": { + "rates": { + "MXN": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Currency conversion rate not found: 'JPY' => 'USD'", + "comparison": "literal" + } + ] +} diff --git a/adapters/adverxo/adverxotest/supplemental/invalid_mtype.json b/adapters/adverxo/adverxotest/supplemental/invalid_mtype.json new file mode 100644 index 0000000000..5fcdee305f --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/invalid_mtype.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=520&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + ], + "expectedMakeBidsErrors": [ + { + "value": "unsupported MType 0", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/invalid_native_adm.json b/adapters/adverxo/adverxotest/supplemental/invalid_native_adm.json new file mode 100644 index 0000000000..90694752e5 --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/invalid_native_adm.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 18, + "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=18&auth=fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 18, + "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "invalid", + "h": 600, + "w": 300, + "mtype": 4 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + ], + "expectedMakeBidsErrors": [ + { + "value": "unable to unmarshal native adm", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/multi_imps.json b/adapters/adverxo/adverxotest/supplemental/multi_imps.json new file mode 100644 index 0000000000..f5eb56565f --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/multi_imps.json @@ -0,0 +1,203 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + }, + { + "id": "imp_id2", + "banner": { + "w": 400, + "h": 800 + }, + "ext": { + "bidder": { + "adUnitId": 2, + "auth": "tokenExample2" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=1&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "impIDs":["imp_id1"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 600, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id" + } + } + }, + { + "expectedRequest": { + "uri": "https://example.com/auction?id=2&auth=tokenExample2", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id2", + "banner": { + "w": 400, + "h": 800 + }, + "ext": { + "bidder": { + "adUnitId": 2, + "auth": "tokenExample2" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "impIDs":["imp_id2"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id2", + "impid": "imp_id2", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 800, + "w": 400, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 1 + }, + "type": "banner" + } + ] + }, + { + "bids": [ + { + "bid": { + "id": "bid_id2", + "impid": "imp_id2", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 400, + "h": 800, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/multiformat.json b/adapters/adverxo/adverxotest/supplemental/multiformat.json new file mode 100644 index 0000000000..06f3be7bd0 --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/multiformat.json @@ -0,0 +1,124 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=520&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "content", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/no_bid.json b/adapters/adverxo/adverxotest/supplemental/no_bid.json new file mode 100644 index 0000000000..0585f34204 --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/no_bid.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=1&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 204, + "body": "", + "headers": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/resolve_ortb_macro.json b/adapters/adverxo/adverxotest/supplemental/resolve_ortb_macro.json new file mode 100644 index 0000000000..02840ea0d8 --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/resolve_ortb_macro.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=520&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 520, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content prc=${AUCTION_PRICE}", + "h": 600, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "content prc=1.25", + "crid": "crid", + "w": 300, + "h": 600, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/adverxotest/supplemental/server_error.json b/adapters/adverxo/adverxotest/supplemental/server_error.json new file mode 100644 index 0000000000..47d29364f0 --- /dev/null +++ b/adapters/adverxo/adverxotest/supplemental/server_error.json @@ -0,0 +1,82 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://example.com/auction?id=1&auth=tokenExample1", + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "adUnitId": 1, + "auth": "tokenExample1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "impIDs":["imp_id"] + }, + "mockResponse": { + "status": 500, + "body": "", + "headers": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/adverxo/params_test.go b/adapters/adverxo/params_test.go new file mode 100644 index 0000000000..a871a03c6e --- /dev/null +++ b/adapters/adverxo/params_test.go @@ -0,0 +1,52 @@ +package adverxo + +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-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdverxo, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected adverxo params: %s", validParam) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAdverxo, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{ "adUnitId": 5, "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44"}`, + `{ "adUnitId": 402053, "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44"}`, +} + +var invalidParams = []string{ + ``, + `null`, + `{}`, + `{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`, + `{ "adUnitId": 5}`, + `{ "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44"}`, + `{ "adUnitId": 0, "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44"}`, + `{ "adUnitId": "5", "auth": "fb71a1ec1d4c0b7e3f0a21703fece91d8b65be44"}`, + `{ "adUnitId": 5, "auth": ""}`, + `{ "adUnitId": 5, "auth": "12345"}`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 67e28286f0..cf1d79f1f9 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -29,6 +29,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/adtonos" "github.com/prebid/prebid-server/v3/adapters/adtrgtme" "github.com/prebid/prebid-server/v3/adapters/advangelists" + "github.com/prebid/prebid-server/v3/adapters/adverxo" "github.com/prebid/prebid-server/v3/adapters/adview" "github.com/prebid/prebid-server/v3/adapters/adxcg" "github.com/prebid/prebid-server/v3/adapters/adyoulike" @@ -258,6 +259,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdtelligent: adtelligent.Builder, openrtb_ext.BidderAdTonos: adtonos.Builder, openrtb_ext.BidderAdvangelists: advangelists.Builder, + openrtb_ext.BidderAdverxo: adverxo.Builder, openrtb_ext.BidderAdView: adview.Builder, openrtb_ext.BidderAdxcg: adxcg.Builder, openrtb_ext.BidderAdyoulike: adyoulike.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index f7706ce5c2..4e0baad6d9 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -45,6 +45,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderAdtelligent, BidderAdTonos, BidderAdvangelists, + BidderAdverxo, BidderAdView, BidderAdxcg, BidderAdyoulike, @@ -374,6 +375,7 @@ const ( BidderAdTonos BidderName = "adtonos" BidderAdtelligent BidderName = "adtelligent" BidderAdvangelists BidderName = "advangelists" + BidderAdverxo BidderName = "adverxo" BidderAdView BidderName = "adview" BidderAdxcg BidderName = "adxcg" BidderAdyoulike BidderName = "adyoulike" diff --git a/openrtb_ext/imp_adverxo.go b/openrtb_ext/imp_adverxo.go new file mode 100644 index 0000000000..6c54424f48 --- /dev/null +++ b/openrtb_ext/imp_adverxo.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ImpExtAdverxo struct { + AdUnitId int `json:"adUnitId"` + Auth string `json:"auth"` +} diff --git a/static/bidder-info/adverxo.yaml b/static/bidder-info/adverxo.yaml new file mode 100644 index 0000000000..c088490d22 --- /dev/null +++ b/static/bidder-info/adverxo.yaml @@ -0,0 +1,22 @@ +endpoint: "https://pbsadverxo.com/auction?id={{.AdUnit}}&auth={{.TokenID}}" +endpointCompression: gzip +maintainer: + email: "developer@adverxo.com" +capabilities: + app: + mediaTypes: + - banner + - native + - video + site: + mediaTypes: + - banner + - native + - video +userSync: + iframe: + url: https://pbsadverxo.com/usync?type=iframe&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID + redirect: + url: https://pbsadverxo.com/usync?type=image&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID diff --git a/static/bidder-params/adverxo.json b/static/bidder-params/adverxo.json new file mode 100644 index 0000000000..72a1b72739 --- /dev/null +++ b/static/bidder-params/adverxo.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adverxo Adapter Params", + "description": "A schema which validates params accepted by the Adverxo adapter", + "type": "object", + "properties": { + "adUnitId": { + "type": "integer", + "minimum": 1, + "description": "Unique identifier for the ad unit in Adverxo platform." + }, + "auth": { + "type": "string", + "minLength": 6, + "description": "Authentication token provided by Adverxo platform for the AdUnit." + } + }, + "required": ["adUnitId", "auth"] + } \ No newline at end of file