Skip to content

Commit 502a438

Browse files
authored
New Adapter: AdUp Tech (#4076)
1 parent 7045683 commit 502a438

21 files changed

+1435
-0
lines changed

adapters/aduptech/aduptech.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package aduptech
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
"strings"
8+
9+
"github.com/prebid/openrtb/v20/openrtb2"
10+
"github.com/prebid/prebid-server/v3/adapters"
11+
"github.com/prebid/prebid-server/v3/config"
12+
prebidcurrency "github.com/prebid/prebid-server/v3/currency"
13+
"github.com/prebid/prebid-server/v3/errortypes"
14+
"github.com/prebid/prebid-server/v3/openrtb_ext"
15+
"github.com/prebid/prebid-server/v3/util/jsonutil"
16+
"golang.org/x/text/currency"
17+
)
18+
19+
type adapter struct {
20+
endpoint string
21+
extraInfo ExtraInfo
22+
}
23+
24+
type ExtraInfo struct {
25+
TargetCurrency string `json:"target_currency,omitempty"`
26+
}
27+
28+
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
29+
var extraInfo ExtraInfo
30+
if err := jsonutil.Unmarshal([]byte(config.ExtraAdapterInfo), &extraInfo); err != nil {
31+
return nil, fmt.Errorf("invalid extra info: %w", err)
32+
}
33+
34+
if extraInfo.TargetCurrency == "" {
35+
return nil, errors.New("invalid extra info: TargetCurrency is empty, pls check")
36+
}
37+
38+
parsedCurrency, err := currency.ParseISO(extraInfo.TargetCurrency)
39+
if err != nil {
40+
return nil, fmt.Errorf("invalid extra info: invalid TargetCurrency %s, pls check", extraInfo.TargetCurrency)
41+
}
42+
extraInfo.TargetCurrency = parsedCurrency.String()
43+
44+
bidder := &adapter{
45+
endpoint: config.Endpoint,
46+
extraInfo: extraInfo,
47+
}
48+
49+
return bidder, nil
50+
}
51+
52+
func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
53+
for i := range request.Imp {
54+
imp := &request.Imp[i]
55+
// Check if imp comes with bid floor amount defined in a foreign currency
56+
if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != a.extraInfo.TargetCurrency {
57+
58+
convertedValue, err := a.convertCurrency(imp.BidFloor, imp.BidFloorCur, reqInfo)
59+
if err != nil {
60+
return nil, []error{err}
61+
}
62+
63+
imp.BidFloorCur = a.extraInfo.TargetCurrency
64+
imp.BidFloor = convertedValue
65+
}
66+
}
67+
68+
requestJSON, err := jsonutil.Marshal(request)
69+
if err != nil {
70+
return nil, []error{err}
71+
}
72+
73+
requestData := &adapters.RequestData{
74+
Method: http.MethodPost,
75+
Uri: a.endpoint,
76+
Body: requestJSON,
77+
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
78+
}
79+
80+
return []*adapters.RequestData{requestData}, nil
81+
}
82+
83+
// convertCurrency attempts to convert a given value from the specified currency (cur) to the
84+
// target currency specified in the adapter's extraInfo. If the conversion directly to the
85+
// target currency fails due to a ConversionNotFoundError, it attempts an intermediate conversion
86+
// through USD. Returns the converted value or an error if conversion fails.
87+
func (a *adapter) convertCurrency(value float64, cur string, reqInfo *adapters.ExtraRequestInfo) (float64, error) {
88+
convertedValue, err := reqInfo.ConvertCurrency(value, cur, a.extraInfo.TargetCurrency)
89+
90+
if err != nil {
91+
var convErr prebidcurrency.ConversionNotFoundError
92+
if !errors.As(err, &convErr) {
93+
return 0, err
94+
}
95+
96+
// try again by first converting to USD
97+
// then convert to target_currency
98+
convertedValue, err = reqInfo.ConvertCurrency(value, cur, "USD")
99+
100+
if err != nil {
101+
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)
102+
}
103+
104+
convertedValue, err = reqInfo.ConvertCurrency(convertedValue, "USD", a.extraInfo.TargetCurrency)
105+
106+
if err != nil {
107+
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)
108+
}
109+
}
110+
return convertedValue, nil
111+
}
112+
113+
func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
114+
if adapters.IsResponseStatusCodeNoContent(responseData) {
115+
return nil, nil
116+
}
117+
118+
if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
119+
return nil, []error{err}
120+
}
121+
122+
var response openrtb2.BidResponse
123+
if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
124+
return nil, []error{err}
125+
}
126+
127+
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
128+
bidResponse.Currency = response.Cur
129+
130+
var errs []error
131+
for i := range response.SeatBid {
132+
seatBid := &response.SeatBid[i]
133+
for j := range seatBid.Bid {
134+
bid := &seatBid.Bid[j]
135+
bidType, err := getBidType(bid.MType)
136+
if err != nil {
137+
errs = append(errs, err)
138+
continue
139+
}
140+
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
141+
Bid: bid,
142+
BidType: bidType,
143+
})
144+
}
145+
}
146+
147+
return bidResponse, errs
148+
}
149+
150+
func getBidType(markupType openrtb2.MarkupType) (openrtb_ext.BidType, error) {
151+
switch markupType {
152+
case openrtb2.MarkupNative:
153+
return openrtb_ext.BidTypeNative, nil
154+
case openrtb2.MarkupBanner:
155+
return openrtb_ext.BidTypeBanner, nil
156+
default:
157+
return "", &errortypes.BadServerResponse{
158+
Message: fmt.Sprintf("Unknown markup type: %d", markupType),
159+
}
160+
}
161+
}

adapters/aduptech/aduptech_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package aduptech
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/prebid/prebid-server/v3/adapters/adapterstest"
10+
"github.com/prebid/prebid-server/v3/config"
11+
"github.com/prebid/prebid-server/v3/openrtb_ext"
12+
)
13+
14+
func TestJsonSamples(t *testing.T) {
15+
bidder, buildErr := Builder(
16+
openrtb_ext.BidderAdUpTech,
17+
config.Adapter{
18+
Endpoint: "https://example.com/rtb/bid",
19+
ExtraAdapterInfo: `{"target_currency": "EUR"}`,
20+
},
21+
config.Server{
22+
ExternalUrl: "http://hosturl.com",
23+
GvlID: 1,
24+
DataCenter: "2",
25+
},
26+
)
27+
28+
require.NoError(t, buildErr, "Builder returned unexpected error")
29+
30+
adapterstest.RunJSONBidderTest(t, "aduptechtest", bidder)
31+
}
32+
33+
func TestInvalidExtraAdapterInfo(t *testing.T) {
34+
_, buildErr := Builder(
35+
openrtb_ext.BidderAdUpTech,
36+
config.Adapter{
37+
Endpoint: "https://example.com/rtb/bid",
38+
ExtraAdapterInfo: `{"foo": "bar"}`,
39+
},
40+
config.Server{
41+
ExternalUrl: "http://hosturl.com",
42+
GvlID: 1,
43+
DataCenter: "2",
44+
},
45+
)
46+
47+
assert.EqualError(t, buildErr, "invalid extra info: TargetCurrency is empty, pls check")
48+
}
49+
50+
func TestInvalidTargetCurrency(t *testing.T) {
51+
_, buildErr := Builder(
52+
openrtb_ext.BidderAdUpTech,
53+
config.Adapter{
54+
Endpoint: "https://example.com/rtb/bid",
55+
ExtraAdapterInfo: `{"target_currency": "INVALID"}`,
56+
},
57+
config.Server{
58+
ExternalUrl: "http://hosturl.com",
59+
GvlID: 1,
60+
DataCenter: "2",
61+
},
62+
)
63+
64+
assert.EqualError(t, buildErr, "invalid extra info: invalid TargetCurrency INVALID, pls check")
65+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
{
2+
"mockBidRequest": {
3+
"id": "test-request-id",
4+
"site": {
5+
"page": "https://good.site/url"
6+
},
7+
"imp": [{
8+
"id": "test-imp-id",
9+
"bidfloor": 1.0,
10+
"bidfloorcur": "AUD",
11+
"banner": {
12+
"format": [{
13+
"w": 300,
14+
"h": 250
15+
}]
16+
},
17+
"ext": {
18+
"bidder": {
19+
"publisher": "123456",
20+
"placement": "234567"
21+
}
22+
}
23+
}],
24+
"ext": {
25+
"prebid": {
26+
"currency": {
27+
"rates": {
28+
"EUR": {
29+
"USD": 0.05
30+
},
31+
"USD": {
32+
"AUD": 2.0
33+
}
34+
},
35+
"usepbsrates": false
36+
}
37+
}
38+
}
39+
},
40+
"httpCalls": [{
41+
"expectedRequest": {
42+
"uri": "https://example.com/rtb/bid",
43+
"body": {
44+
"id": "test-request-id",
45+
"site": {
46+
"page": "https://good.site/url"
47+
},
48+
"imp": [
49+
{
50+
"id": "test-imp-id",
51+
"bidfloor": 10,
52+
"bidfloorcur": "EUR",
53+
"banner": {
54+
"format": [
55+
{
56+
"w": 300,
57+
"h": 250
58+
}
59+
]
60+
},
61+
"ext": {
62+
"bidder": {
63+
"publisher": "123456",
64+
"placement": "234567"
65+
}
66+
}
67+
}
68+
],
69+
"ext": {
70+
"prebid": {
71+
"currency": {
72+
"rates": {
73+
"EUR": {
74+
"USD": 0.05
75+
},
76+
"USD": {
77+
"AUD": 2.0
78+
}
79+
},
80+
"usepbsrates": false
81+
}
82+
}
83+
}
84+
},
85+
"impIDs":["test-imp-id"]
86+
},
87+
"mockResponse": {
88+
"status": 200,
89+
"body": {
90+
"id": "test-request-id",
91+
"seatbid": [
92+
{
93+
"seat": "958",
94+
"bid": [{
95+
"id": "7706636740145184841",
96+
"impid": "test-imp-id",
97+
"price": 0.500000,
98+
"adm": "some-test-ad",
99+
"adomain": ["example.com"],
100+
"crid": "29681110",
101+
"h": 250,
102+
"w": 300,
103+
"mtype": 1
104+
}]
105+
}
106+
],
107+
"cur": "EUR"
108+
}
109+
}
110+
}],
111+
112+
"expectedBidResponses": [{
113+
"currency": "EUR",
114+
"bids": [{
115+
"bid": {
116+
"id": "7706636740145184841",
117+
"impid": "test-imp-id",
118+
"price": 0.500000,
119+
"adm": "some-test-ad",
120+
"adomain": ["example.com"],
121+
"crid": "29681110",
122+
"h": 250,
123+
"w": 300,
124+
"mtype": 1
125+
},
126+
"type": "banner"
127+
}]
128+
}]
129+
}

0 commit comments

Comments
 (0)