From 502a4385a5dcd4da51fc16a24627da50ec84a954 Mon Sep 17 00:00:00 2001 From: Dany Gielow Date: Thu, 30 Jan 2025 18:14:07 +0100 Subject: [PATCH] New Adapter: AdUp Tech (#4076) --- adapters/aduptech/aduptech.go | 161 ++++++++++++++++++ adapters/aduptech/aduptech_test.go | 65 +++++++ ...-banner-convert-currency-intermediate.json | 129 ++++++++++++++ .../simple-banner-convert-currency.json | 123 +++++++++++++ .../exemplary/simple-banner-full-params.json | 107 ++++++++++++ .../aduptechtest/exemplary/simple-banner.json | 95 +++++++++++ .../aduptechtest/exemplary/simple-native.json | 118 +++++++++++++ .../supplemental/broken-response.json | 84 +++++++++ .../currency-conversion-failure.json | 45 +++++ ...rency-conversion-intermediate-failure.json | 46 +++++ .../currency-conversion-invalid-currency.json | 45 +++++ .../aduptechtest/supplemental/status-204.json | 54 ++++++ .../aduptechtest/supplemental/status-400.json | 59 +++++++ .../aduptechtest/supplemental/status-500.json | 59 +++++++ .../supplemental/unsupported-mtype.json | 91 ++++++++++ adapters/aduptech/params_test.go | 74 ++++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_aduptech.go | 12 ++ static/bidder-info/aduptech.yaml | 27 +++ static/bidder-params/aduptech.json | 37 ++++ 21 files changed, 1435 insertions(+) create mode 100644 adapters/aduptech/aduptech.go create mode 100644 adapters/aduptech/aduptech_test.go create mode 100644 adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency-intermediate.json create mode 100644 adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency.json create mode 100644 adapters/aduptech/aduptechtest/exemplary/simple-banner-full-params.json create mode 100644 adapters/aduptech/aduptechtest/exemplary/simple-banner.json create mode 100644 adapters/aduptech/aduptechtest/exemplary/simple-native.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/broken-response.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/currency-conversion-failure.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/currency-conversion-intermediate-failure.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/currency-conversion-invalid-currency.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/status-204.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/status-400.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/status-500.json create mode 100644 adapters/aduptech/aduptechtest/supplemental/unsupported-mtype.json create mode 100644 adapters/aduptech/params_test.go create mode 100644 openrtb_ext/imp_aduptech.go create mode 100644 static/bidder-info/aduptech.yaml create mode 100644 static/bidder-params/aduptech.json diff --git a/adapters/aduptech/aduptech.go b/adapters/aduptech/aduptech.go new file mode 100644 index 00000000000..dee3e679ef9 --- /dev/null +++ b/adapters/aduptech/aduptech.go @@ -0,0 +1,161 @@ +package aduptech + +import ( + "errors" + "fmt" + "net/http" + "strings" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/config" + prebidcurrency "github.com/prebid/prebid-server/v3/currency" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" + "golang.org/x/text/currency" +) + +type adapter struct { + endpoint string + extraInfo ExtraInfo +} + +type ExtraInfo struct { + TargetCurrency string `json:"target_currency,omitempty"` +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + var extraInfo ExtraInfo + if err := jsonutil.Unmarshal([]byte(config.ExtraAdapterInfo), &extraInfo); err != nil { + return nil, fmt.Errorf("invalid extra info: %w", err) + } + + if extraInfo.TargetCurrency == "" { + return nil, errors.New("invalid extra info: TargetCurrency is empty, pls check") + } + + parsedCurrency, err := currency.ParseISO(extraInfo.TargetCurrency) + if err != nil { + return nil, fmt.Errorf("invalid extra info: invalid TargetCurrency %s, pls check", extraInfo.TargetCurrency) + } + extraInfo.TargetCurrency = parsedCurrency.String() + + bidder := &adapter{ + endpoint: config.Endpoint, + extraInfo: extraInfo, + } + + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + for i := range request.Imp { + imp := &request.Imp[i] + // Check if imp comes with bid floor amount defined in a foreign currency + if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != a.extraInfo.TargetCurrency { + + convertedValue, err := a.convertCurrency(imp.BidFloor, imp.BidFloorCur, reqInfo) + if err != nil { + return nil, []error{err} + } + + imp.BidFloorCur = a.extraInfo.TargetCurrency + imp.BidFloor = convertedValue + } + } + + requestJSON, err := jsonutil.Marshal(request) + if err != nil { + return nil, []error{err} + } + + requestData := &adapters.RequestData{ + Method: http.MethodPost, + Uri: a.endpoint, + Body: requestJSON, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + } + + return []*adapters.RequestData{requestData}, nil +} + +// convertCurrency attempts to convert a given value from the specified currency (cur) to the +// target currency specified in the adapter's extraInfo. If the conversion directly to the +// target currency fails due to a ConversionNotFoundError, it attempts an intermediate conversion +// through USD. Returns the converted value or an error if conversion fails. +func (a *adapter) convertCurrency(value float64, cur string, reqInfo *adapters.ExtraRequestInfo) (float64, error) { + convertedValue, err := reqInfo.ConvertCurrency(value, cur, a.extraInfo.TargetCurrency) + + if err != nil { + var convErr prebidcurrency.ConversionNotFoundError + if !errors.As(err, &convErr) { + return 0, err + } + + // try again by first converting to USD + // then convert to target_currency + convertedValue, err = reqInfo.ConvertCurrency(value, cur, "USD") + + if err != nil { + return 0, fmt.Errorf("Currency conversion rate not found from '%s' to '%s'. Error converting from '%s' to 'USD': %w", cur, a.extraInfo.TargetCurrency, cur, err) + } + + convertedValue, err = reqInfo.ConvertCurrency(convertedValue, "USD", a.extraInfo.TargetCurrency) + + if err != nil { + return 0, fmt.Errorf("Currency conversion rate not found from '%s' to '%s'. Error converting from 'USD' to '%s': %w", cur, a.extraInfo.TargetCurrency, a.extraInfo.TargetCurrency, err) + } + } + return convertedValue, nil +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(responseData) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil { + return nil, []error{err} + } + + var response openrtb2.BidResponse + if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + bidResponse.Currency = response.Cur + + var errs []error + for i := range response.SeatBid { + seatBid := &response.SeatBid[i] + for j := range seatBid.Bid { + bid := &seatBid.Bid[j] + bidType, err := getBidType(bid.MType) + if err != nil { + errs = append(errs, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: bid, + BidType: bidType, + }) + } + } + + return bidResponse, errs +} + +func getBidType(markupType openrtb2.MarkupType) (openrtb_ext.BidType, error) { + switch markupType { + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + default: + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unknown markup type: %d", markupType), + } + } +} diff --git a/adapters/aduptech/aduptech_test.go b/adapters/aduptech/aduptech_test.go new file mode 100644 index 00000000000..64dc6b3d41c --- /dev/null +++ b/adapters/aduptech/aduptech_test.go @@ -0,0 +1,65 @@ +package aduptech + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder( + openrtb_ext.BidderAdUpTech, + config.Adapter{ + Endpoint: "https://example.com/rtb/bid", + ExtraAdapterInfo: `{"target_currency": "EUR"}`, + }, + config.Server{ + ExternalUrl: "http://hosturl.com", + GvlID: 1, + DataCenter: "2", + }, + ) + + require.NoError(t, buildErr, "Builder returned unexpected error") + + adapterstest.RunJSONBidderTest(t, "aduptechtest", bidder) +} + +func TestInvalidExtraAdapterInfo(t *testing.T) { + _, buildErr := Builder( + openrtb_ext.BidderAdUpTech, + config.Adapter{ + Endpoint: "https://example.com/rtb/bid", + ExtraAdapterInfo: `{"foo": "bar"}`, + }, + config.Server{ + ExternalUrl: "http://hosturl.com", + GvlID: 1, + DataCenter: "2", + }, + ) + + assert.EqualError(t, buildErr, "invalid extra info: TargetCurrency is empty, pls check") +} + +func TestInvalidTargetCurrency(t *testing.T) { + _, buildErr := Builder( + openrtb_ext.BidderAdUpTech, + config.Adapter{ + Endpoint: "https://example.com/rtb/bid", + ExtraAdapterInfo: `{"target_currency": "INVALID"}`, + }, + config.Server{ + ExternalUrl: "http://hosturl.com", + GvlID: 1, + DataCenter: "2", + }, + ) + + assert.EqualError(t, buildErr, "invalid extra info: invalid TargetCurrency INVALID, pls check") +} diff --git a/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency-intermediate.json b/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency-intermediate.json new file mode 100644 index 00000000000..c50f56f00bb --- /dev/null +++ b/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency-intermediate.json @@ -0,0 +1,129 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "bidfloor": 1.0, + "bidfloorcur": "AUD", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + }, + "USD": { + "AUD": 2.0 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "bidfloor": 10, + "bidfloorcur": "EUR", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + } + ], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + }, + "USD": { + "AUD": 2.0 + } + }, + "usepbsrates": false + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }] + } + ], + "cur": "EUR" + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency.json b/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency.json new file mode 100644 index 00000000000..05a0016f45b --- /dev/null +++ b/adapters/aduptech/aduptechtest/exemplary/simple-banner-convert-currency.json @@ -0,0 +1,123 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "bidfloor": 1.0, + "bidfloorcur": "USD", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "bidfloor": 20, + "bidfloorcur": "EUR", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + } + ], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }] + } + ], + "cur": "EUR" + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/aduptech/aduptechtest/exemplary/simple-banner-full-params.json b/adapters/aduptech/aduptechtest/exemplary/simple-banner-full-params.json new file mode 100644 index 00000000000..29a8c888c17 --- /dev/null +++ b/adapters/aduptech/aduptechtest/exemplary/simple-banner-full-params.json @@ -0,0 +1,107 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567", + "adtest": true, + "debug": true, + "query": "abc,def", + "ext": { + "foo": "bar" + } + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567", + "adtest": true, + "debug": true, + "query": "abc,def", + "ext": { + "foo": "bar" + } + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }] + } + ], + "cur": "EUR" + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/aduptech/aduptechtest/exemplary/simple-banner.json b/adapters/aduptech/aduptechtest/exemplary/simple-banner.json new file mode 100644 index 00000000000..69c7655418c --- /dev/null +++ b/adapters/aduptech/aduptechtest/exemplary/simple-banner.json @@ -0,0 +1,95 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }] + } + ], + "cur": "EUR" + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/aduptech/aduptechtest/exemplary/simple-native.json b/adapters/aduptech/aduptechtest/exemplary/simple-native.json new file mode 100644 index 00000000000..2c1086897fe --- /dev/null +++ b/adapters/aduptech/aduptechtest/exemplary/simple-native.json @@ -0,0 +1,118 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [{ + "seat": "grid", + "bid": [{ + "id": "some-id", + "impid": "test-imp-id", + "price": 0.010526, + "adm": "{\"native\":{\"ver\":\"1.2\",\"link\":{\"url\":\"some-url\"},\"assets\":[{\"title\":{\"text\":\"some title\"},\"id\":123},{\"img\":{\"url\":\"some-image\",\"w\":300,\"h\":250},\"id\":128},{\"data\":{\"value\":\"some-description\"},\"id\":127}],\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"some-click-url\"}],\"privacy\":\"https://www.adup-tech.com/oba/\"}}", + "adomain": [ + "example.com" + ], + "crid": "12345678", + "mtype": 4, + "ext": { + "dsa": { + "adrender": 0, + "behalf": "example.com", + "paid": "Example Site", + "transparency": [ + { + "domain": "adup-tech.com", + "params": [ + 2 + ] + } + ] + } + } + }] + }], + "currency": "EUR" + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "some-id", + "impid": "test-imp-id", + "price": 0.010526, + "adm": "{\"native\":{\"ver\":\"1.2\",\"link\":{\"url\":\"some-url\"},\"assets\":[{\"title\":{\"text\":\"some title\"},\"id\":123},{\"img\":{\"url\":\"some-image\",\"w\":300,\"h\":250},\"id\":128},{\"data\":{\"value\":\"some-description\"},\"id\":127}],\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"some-click-url\"}],\"privacy\":\"https://www.adup-tech.com/oba/\"}}", + "adomain": [ + "example.com" + ], + "crid": "12345678", + "mtype": 4, + "ext": { + "dsa": { + "adrender": 0, + "behalf": "example.com", + "paid": "Example Site", + "transparency": [ + { + "domain": "adup-tech.com", + "params": [ + 2 + ] + } + ] + } + } + }, + "type": "native" + }] + }] +} diff --git a/adapters/aduptech/aduptechtest/supplemental/broken-response.json b/adapters/aduptech/aduptechtest/supplemental/broken-response.json new file mode 100644 index 00000000000..c97da5a88fa --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/broken-response.json @@ -0,0 +1,84 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [{ + "id":123, + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "adomain": ["example.com"], + "crid": "29681110", + "h": 250, + "w": 300, + "mtype": 1 + }] + } + ], + "cur": "EUR" + } + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "cannot unmarshal openrtb2.Bid.ID: expects \" or n, but found 1", + "comparison": "literal" + } + ] +} diff --git a/adapters/aduptech/aduptechtest/supplemental/currency-conversion-failure.json b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-failure.json new file mode 100644 index 00000000000..bb818faa273 --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-failure.json @@ -0,0 +1,45 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "bidfloor": 1.0, + "bidfloorcur": "AUD", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }], + "ext": { + "prebid": { + "currency": { + "rates": { + "USD": { + "AUD": 2.0 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "Currency conversion rate not found from 'AUD' to 'EUR'. Error converting from 'USD' to 'EUR': Currency conversion rate not found: 'USD' => 'EUR'", + "comparison": "literal" + } + ] +} diff --git a/adapters/aduptech/aduptechtest/supplemental/currency-conversion-intermediate-failure.json b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-intermediate-failure.json new file mode 100644 index 00000000000..a92fc2b4afe --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-intermediate-failure.json @@ -0,0 +1,46 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "bidfloor": 1.0, + "bidfloorcur": "AUD", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "Currency conversion rate not found from 'AUD' to 'EUR'. Error converting from 'AUD' to 'USD': Currency conversion rate not found: 'AUD' => 'USD'", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/aduptech/aduptechtest/supplemental/currency-conversion-invalid-currency.json b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-invalid-currency.json new file mode 100644 index 00000000000..d9d9e1e59ed --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/currency-conversion-invalid-currency.json @@ -0,0 +1,45 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "bidfloor": 1.0, + "bidfloorcur": "INVALID", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }], + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 0.05 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [], + "expectedBidResponses": [], + "expectedMakeRequestsErrors": [ + { + "value": "currency: tag is not well-formed", + "comparison": "literal" + } + ] +} diff --git a/adapters/aduptech/aduptechtest/supplemental/status-204.json b/adapters/aduptech/aduptechtest/supplemental/status-204.json new file mode 100644 index 00000000000..2fbe824cfb4 --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/status-204.json @@ -0,0 +1,54 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} diff --git a/adapters/aduptech/aduptechtest/supplemental/status-400.json b/adapters/aduptech/aduptechtest/supplemental/status-400.json new file mode 100644 index 00000000000..6a6d3f031b4 --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/status-400.json @@ -0,0 +1,59 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "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/aduptech/aduptechtest/supplemental/status-500.json b/adapters/aduptech/aduptechtest/supplemental/status-500.json new file mode 100644 index 00000000000..d7cf7ad07a9 --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/status-500.json @@ -0,0 +1,59 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "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/aduptech/aduptechtest/supplemental/unsupported-mtype.json b/adapters/aduptech/aduptechtest/supplemental/unsupported-mtype.json new file mode 100644 index 00000000000..5d4ab7bd7cb --- /dev/null +++ b/adapters/aduptech/aduptechtest/supplemental/unsupported-mtype.json @@ -0,0 +1,91 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "https://example.com/rtb/bid", + "body": { + "id": "test-request-id", + "site": { + "id": "123", + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "native": { + "ver": "1.2", + "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":4,\"plcmtcnt\":1,\"urlsupport\":0,\"privacy\":1,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":300,\"hmin\":250,\"type\":3}},{\"id\":129,\"required\":0,\"img\":{\"type\":2}},{\"id\":127,\"required\":1,\"data\":{\"type\":2,\"len\":140}}]}" + }, + "ext": { + "bidder": { + "publisher": "123456", + "placement": "234567" + } + } + }] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [{ + "seat": "grid", + "bid": [{ + "id": "some-id", + "impid": "test-imp-id", + "price": 0.010526, + "adomain": [ + "example.com" + ], + "crid": "12345678", + "mtype": 3, + "ext": { + "dsa": { + "adrender": 0, + "behalf": "example.com", + "paid": "Example Site", + "transparency": [ + { + "domain": "adup-tech.com", + "params": [ + 2 + ] + } + ] + } + } + }] + }], + "currency": "EUR" + } + } + }], + "expectedBidResponses": [{"currency":"EUR","bids":[]}], + "expectedMakeBidsErrors": [ + { + "value": "Unknown markup type: 3", + "comparison": "literal" + } + ] +} diff --git a/adapters/aduptech/params_test.go b/adapters/aduptech/params_test.go new file mode 100644 index 00000000000..ce0ae7d1265 --- /dev/null +++ b/adapters/aduptech/params_test.go @@ -0,0 +1,74 @@ +package aduptech + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +var validParams = []string{ + `{ "publisher": "1", "placement": "2" }`, + `{ "publisher": "123456789", "placement": "234567890" }`, + `{ "publisher": "123456789", "placement": "234567890", "query": "" }`, + `{ "publisher": "123456789", "placement": "234567890", "query": "test" }`, + `{ "publisher": "123456789", "placement": "234567890", "adtest": true }`, + `{ "publisher": "123456789", "placement": "234567890", "debug": true }`, + `{ "publisher": "123456789", "placement": "234567890", "query": "test", "adtest": true }`, + `{ "publisher": "123456789", "placement": "234567890", "ext": {"foo": "bar"} }`, + `{ "publisher": "123456789", "placement": "234567890", "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 { + assert.NoErrorf( + t, + validator.Validate(openrtb_ext.BidderAdUpTech, json.RawMessage(validParam)), + "Schema rejected Aduptech params: %s", + validParam, + ) + } +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{ "publisher": "123456789" }`, + `{ "placement": "234567890" }`, + `{ "publisher": null, "placement": null }`, + `{ "publisher": "", "placement": "" }`, + `{ "publisher": "123456789", "placement": "234567890", "query": null }`, + `{ "publisher": "123456789", "placement": "234567890", "adtest": null }`, + `{ "publisher": "123456789", "placement": "234567890", "debug": null }`, + `{ "publisher": "123456789", "placement": "234567890", "ext": null }`, + `{ "publisher": "123456789", "placement": "234567890", "ext": 123 }`, + `{ "publisher": "123456789", "placement": "234567890", "ext": "abc" }`, +} + +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 { + assert.Errorf( + t, + validator.Validate(openrtb_ext.BidderAdUpTech, json.RawMessage(invalidParam)), + "Schema allowed unexpected params: %s", + invalidParam, + ) + } +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 240fb504073..689ad4dcfa9 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -29,6 +29,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/adtelligent" "github.com/prebid/prebid-server/v3/adapters/adtonos" "github.com/prebid/prebid-server/v3/adapters/adtrgtme" + "github.com/prebid/prebid-server/v3/adapters/aduptech" "github.com/prebid/prebid-server/v3/adapters/advangelists" "github.com/prebid/prebid-server/v3/adapters/adverxo" "github.com/prebid/prebid-server/v3/adapters/adview" @@ -269,6 +270,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdtrgtme: adtrgtme.Builder, openrtb_ext.BidderAdtelligent: adtelligent.Builder, openrtb_ext.BidderAdTonos: adtonos.Builder, + openrtb_ext.BidderAdUpTech: aduptech.Builder, openrtb_ext.BidderAdvangelists: advangelists.Builder, openrtb_ext.BidderAdverxo: adverxo.Builder, openrtb_ext.BidderAdView: adview.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 40231140545..8c6f01cf897 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -45,6 +45,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderAdtrgtme, BidderAdtelligent, BidderAdTonos, + BidderAdUpTech, BidderAdvangelists, BidderAdverxo, BidderAdView, @@ -392,6 +393,7 @@ const ( BidderAdtrgtme BidderName = "adtrgtme" BidderAdTonos BidderName = "adtonos" BidderAdtelligent BidderName = "adtelligent" + BidderAdUpTech BidderName = "aduptech" BidderAdvangelists BidderName = "advangelists" BidderAdverxo BidderName = "adverxo" BidderAdView BidderName = "adview" diff --git a/openrtb_ext/imp_aduptech.go b/openrtb_ext/imp_aduptech.go new file mode 100644 index 00000000000..b91b1d0da0d --- /dev/null +++ b/openrtb_ext/imp_aduptech.go @@ -0,0 +1,12 @@ +package openrtb_ext + +import "encoding/json" + +type ExtImpAduptech struct { + Publisher string `json:"publisher"` + Placement string `json:"placement"` + Query string `json:"query"` + AdTest bool `json:"adtest"` + Debug bool `json:"debug,omitempty"` + Ext json.RawMessage `json:"ext,omitempty"` +} diff --git a/static/bidder-info/aduptech.yaml b/static/bidder-info/aduptech.yaml new file mode 100644 index 00000000000..8c1e426c850 --- /dev/null +++ b/static/bidder-info/aduptech.yaml @@ -0,0 +1,27 @@ +endpoint: "https://rtb.d.adup-tech.com/rtb/bid" +extra_info: "{\"target_currency\": \"EUR\"}" +geoscope: + - EEA + - CHE + - GBR +maintainer: + email: "support@adup-tech.com" +gvlVendorID: 647 +openrtb: + version: 2.6 +capabilities: + app: + mediaTypes: + - banner + - native + site: + mediaTypes: + - banner + - native +userSync: + redirect: + url: "https://rtb.d.adup-tech.com/service/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}" + userMacro: "[UID]" + iframe: + url: "https://rtb.d.adup-tech.com/service/sync?iframe=1&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}" + userMacro: "[UID]" \ No newline at end of file diff --git a/static/bidder-params/aduptech.json b/static/bidder-params/aduptech.json new file mode 100644 index 00000000000..98c5086d5bf --- /dev/null +++ b/static/bidder-params/aduptech.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AdUp Tech adapter params", + "description": "A schema which validates params accepted by the AdUp Tech adapter", + "type": "object", + + "properties": { + "publisher": { + "type": "string", + "minLength": 1, + "description": "Unique publisher identifier." + }, + "placement": { + "type": "string", + "minLength": 1, + "description": "Unique placement identifier per publisher." + }, + "query": { + "type": "string", + "description": "Semicolon separated list of keywords." + }, + "adtest": { + "type": "boolean", + "description": "Deactivates tracking of impressions and clicks. **Should only be used for testing purposes!**" + }, + "debug": { + "type": "boolean", + "description": "Enables debug mode. **Should only be used for testing purposes!**" + }, + "ext": { + "type": "object", + "description": "Additional parameters to be included in the request." + } + }, + + "required": ["publisher", "placement"] + } \ No newline at end of file