Skip to content

Commit 1e1aec0

Browse files
authored
Revert "ResetDigital: Switch to OpenRTB (#4385)" (#4438)
1 parent cfef0da commit 1e1aec0

8 files changed

Lines changed: 416 additions & 295 deletions

File tree

Lines changed: 109 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,182 @@
11
package org.prebid.server.bidder.resetdigital;
22

3-
import com.fasterxml.jackson.core.type.TypeReference;
43
import com.iab.openrtb.request.BidRequest;
54
import com.iab.openrtb.request.Imp;
65
import com.iab.openrtb.response.Bid;
76
import com.iab.openrtb.response.BidResponse;
87
import com.iab.openrtb.response.SeatBid;
9-
import io.vertx.core.MultiMap;
108
import org.apache.commons.collections4.CollectionUtils;
119
import org.apache.commons.lang3.StringUtils;
1210
import org.prebid.server.bidder.Bidder;
1311
import org.prebid.server.bidder.model.BidderBid;
1412
import org.prebid.server.bidder.model.BidderCall;
1513
import org.prebid.server.bidder.model.BidderError;
1614
import org.prebid.server.bidder.model.HttpRequest;
15+
import org.prebid.server.bidder.model.Price;
1716
import org.prebid.server.bidder.model.Result;
17+
import org.prebid.server.currency.CurrencyConversionService;
1818
import org.prebid.server.exception.PreBidException;
1919
import org.prebid.server.json.DecodeException;
2020
import org.prebid.server.json.JacksonMapper;
21-
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
22-
import org.prebid.server.proto.openrtb.ext.request.resetdigital.ExtImpResetDigital;
2321
import org.prebid.server.proto.openrtb.ext.response.BidType;
2422
import org.prebid.server.util.BidderUtil;
2523
import org.prebid.server.util.HttpUtil;
2624

25+
import java.math.BigDecimal;
2726
import java.util.ArrayList;
27+
import java.util.Collection;
2828
import java.util.Collections;
2929
import java.util.List;
3030
import java.util.Objects;
31-
import java.util.Optional;
31+
import java.util.stream.Stream;
3232

3333
public class ResetDigitalBidder implements Bidder<BidRequest> {
3434

3535
private static final String DEFAULT_CURRENCY = "USD";
36-
private static final TypeReference<ExtPrebid<?, ExtImpResetDigital>> EXT_TYPE_REFERENCE =
37-
new TypeReference<>() {
38-
};
3936

4037
private final String endpointUrl;
38+
private final CurrencyConversionService currencyConversionService;
4139
private final JacksonMapper mapper;
4240

43-
public ResetDigitalBidder(String endpointUrl, JacksonMapper mapper) {
41+
public ResetDigitalBidder(String endpointUrl,
42+
CurrencyConversionService currencyConversionService,
43+
JacksonMapper mapper) {
44+
4445
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
46+
this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
4547
this.mapper = Objects.requireNonNull(mapper);
4648
}
4749

4850
@Override
4951
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
50-
if (request.getImp().size() != 1) {
51-
return Result.withError(BidderError.badInput(
52-
"ResetDigital adapter supports only one impression per request"));
52+
final List<Imp> bannerImps = new ArrayList<>();
53+
final List<Imp> videoImps = new ArrayList<>();
54+
final List<Imp> audioImps = new ArrayList<>();
55+
Price bidFloorPrice;
56+
57+
for (Imp imp : request.getImp()) {
58+
try {
59+
bidFloorPrice = resolveBidFloor(imp, request);
60+
} catch (PreBidException e) {
61+
return Result.withError(BidderError.badInput(e.getMessage()));
62+
}
63+
populateBannerImps(bannerImps, bidFloorPrice, imp);
64+
populateVideoImps(videoImps, bidFloorPrice, imp);
65+
populateAudiImps(audioImps, bidFloorPrice, imp);
5366
}
5467

55-
final Imp imp = request.getImp().getFirst();
56-
final ExtImpResetDigital extImp;
57-
try {
58-
extImp = parseImpExt(imp);
59-
} catch (PreBidException e) {
60-
return Result.withError(BidderError.badInput(e.getMessage()));
61-
}
68+
return Result.withValues(getHttpRequests(request, bannerImps, videoImps, audioImps));
69+
}
6270

63-
final Imp modifiedImp = modifyImp(imp, extImp);
64-
final BidRequest outgoingRequest = request.toBuilder()
65-
.imp(Collections.singletonList(modifiedImp))
66-
.build();
71+
private List<HttpRequest<BidRequest>> getHttpRequests(BidRequest request,
72+
List<Imp> bannerImps,
73+
List<Imp> videoImps,
74+
List<Imp> audioImps) {
75+
76+
return Stream.of(bannerImps, videoImps, audioImps)
77+
.filter(CollectionUtils::isNotEmpty)
78+
.map(imp -> makeHttpRequest(request, imp))
79+
.toList();
80+
}
6781

68-
final String uri = endpointUrl + "?pid=" + HttpUtil.encodeUrl(extImp.getPlacementId());
69-
final MultiMap headers = HttpUtil.headers()
70-
.add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5");
82+
private HttpRequest<BidRequest> makeHttpRequest(BidRequest bidRequest, List<Imp> imp) {
83+
final BidRequest outgoingRequest = bidRequest.toBuilder().imp(imp).build();
7184

72-
return Result.withValue(BidderUtil.defaultRequest(outgoingRequest, headers, uri, mapper));
85+
return BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
7386
}
7487

75-
private ExtImpResetDigital parseImpExt(Imp imp) {
88+
private static Imp modifyImp(Imp imp, Price bidFloorPrice) {
89+
return imp.toBuilder()
90+
.bidfloorcur(bidFloorPrice.getCurrency())
91+
.bidfloor(bidFloorPrice.getValue())
92+
.build();
93+
}
94+
95+
private Price resolveBidFloor(Imp imp, BidRequest bidRequest) {
96+
final Price initialBidFloorPrice = Price.of(imp.getBidfloorcur(), imp.getBidfloor());
97+
return BidderUtil.isValidPrice(initialBidFloorPrice)
98+
? convertBidFloor(initialBidFloorPrice, imp.getId(), bidRequest)
99+
: initialBidFloorPrice;
100+
}
101+
102+
private Price convertBidFloor(Price bidFloorPrice, String impId, BidRequest bidRequest) {
103+
final String bidFloorCur = bidFloorPrice.getCurrency();
76104
try {
77-
return mapper.mapper().convertValue(imp.getExt(), EXT_TYPE_REFERENCE).getBidder();
78-
} catch (IllegalArgumentException e) {
79-
throw new PreBidException("Error parsing resetDigitalExt from imp.ext: " + e.getMessage());
105+
final BigDecimal convertedPrice = currencyConversionService
106+
.convertCurrency(bidFloorPrice.getValue(), bidRequest, bidFloorCur, DEFAULT_CURRENCY);
107+
108+
return Price.of(DEFAULT_CURRENCY, convertedPrice);
109+
} catch (PreBidException e) {
110+
throw new PreBidException(
111+
"Unable to convert provided bid floor currency from %s to %s for imp `%s`"
112+
.formatted(bidFloorCur, DEFAULT_CURRENCY, impId));
113+
}
114+
}
115+
116+
private static void populateBannerImps(List<Imp> bannerImps, Price bidFloorPrice, Imp imp) {
117+
if (imp.getBanner() != null) {
118+
final Imp bannerImp = imp.toBuilder().video(null).xNative(null).audio(null).build();
119+
bannerImps.add(modifyImp(bannerImp, bidFloorPrice));
120+
}
121+
}
122+
123+
private static void populateVideoImps(List<Imp> videoImps, Price bidFloorPrice, Imp imp) {
124+
if (imp.getVideo() != null) {
125+
final Imp videoImp = imp.toBuilder().banner(null).xNative(null).audio(null).build();
126+
videoImps.add(modifyImp(videoImp, bidFloorPrice));
80127
}
81128
}
82129

83-
private static Imp modifyImp(Imp imp, ExtImpResetDigital extImp) {
84-
return StringUtils.isBlank(imp.getTagid())
85-
? imp.toBuilder().tagid(extImp.getPlacementId()).build()
86-
: imp;
130+
private static void populateAudiImps(List<Imp> audioImps, Price bidFloorPrice, Imp imp) {
131+
if (imp.getAudio() != null) {
132+
final Imp audioImp = imp.toBuilder().banner(null).xNative(null).video(null).build();
133+
audioImps.add(modifyImp(audioImp, bidFloorPrice));
134+
}
87135
}
88136

89137
@Override
90138
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
91139
try {
92-
final List<BidderError> errors = new ArrayList<>();
93140
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
94-
return Result.of(extractBids(bidResponse, httpCall.getRequest().getPayload(), errors), errors);
141+
return Result.withValues(extractBids(bidResponse, httpCall.getRequest().getPayload()));
95142
} catch (DecodeException | PreBidException e) {
96143
return Result.withError(BidderError.badServerResponse(e.getMessage()));
97144
}
98145
}
99146

100-
private static List<BidderBid> extractBids(BidResponse bidResponse,
101-
BidRequest bidRequest,
102-
List<BidderError> errors) {
103-
147+
private static List<BidderBid> extractBids(BidResponse bidResponse, BidRequest bidRequest) {
104148
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
105149
return Collections.emptyList();
106150
}
107-
108-
final Imp imp = bidRequest.getImp().getFirst();
109-
final String currency = StringUtils.isNotBlank(bidResponse.getCur())
110-
? bidResponse.getCur()
111-
: bidRequest.getCur().stream().findFirst().orElse(DEFAULT_CURRENCY);
112-
113-
final List<BidderBid> bidderBids = new ArrayList<>();
114-
for (SeatBid seatBid : bidResponse.getSeatbid()) {
115-
if (seatBid == null || CollectionUtils.isEmpty(seatBid.getBid())) {
116-
continue;
117-
}
118-
119-
for (Bid bid : seatBid.getBid()) {
120-
try {
121-
bidderBids.add(makeBidderBid(bid, seatBid.getSeat(), currency, imp));
122-
} catch (PreBidException e) {
123-
errors.add(BidderError.badServerResponse(e.getMessage()));
124-
}
125-
}
151+
if (bidResponse.getCur() != null && !StringUtils.equalsIgnoreCase(DEFAULT_CURRENCY, bidResponse.getCur())) {
152+
throw new PreBidException("Bidder support only USD currency");
126153
}
127-
128-
return bidderBids;
154+
return bidsFromResponse(bidResponse, bidRequest);
129155
}
130156

131-
private static BidderBid makeBidderBid(Bid bid, String seat, String currency, Imp imp) {
132-
if (!BidderUtil.isValidPrice(bid.getPrice())) {
133-
throw new PreBidException("price %s <= 0 filtered out".formatted(bid.getPrice()));
134-
}
135-
136-
final BidType bidType = Optional.ofNullable(getBidType(bid))
137-
.orElseGet(() -> getBidType(bid, imp));
138-
139-
return StringUtils.isNotBlank(seat)
140-
? BidderBid.of(bid, bidType, seat, currency)
141-
: BidderBid.of(bid, bidType, currency);
142-
}
143-
144-
private static BidType getBidType(Bid bid) {
145-
final Integer mtype = bid.getMtype();
146-
return switch (mtype) {
147-
case 1 -> BidType.banner;
148-
case 2 -> BidType.video;
149-
case 3 -> BidType.audio;
150-
case 4 -> BidType.xNative;
151-
case null -> null;
152-
default -> throw new PreBidException("Unsupported MType: " + mtype);
153-
};
157+
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse, BidRequest bidRequest) {
158+
return bidResponse.getSeatbid().stream()
159+
.filter(Objects::nonNull)
160+
.map(SeatBid::getBid)
161+
.filter(Objects::nonNull)
162+
.flatMap(Collection::stream)
163+
.map(bid -> BidderBid.of(bid, getBidType(bid, bidRequest.getImp()), DEFAULT_CURRENCY))
164+
.toList();
154165
}
155166

156-
private static BidType getBidType(Bid bid, Imp imp) {
157-
if (!imp.getId().equals(bid.getImpid())) {
158-
throw new PreBidException("No matching impression found for ImpID: " + bid.getImpid());
159-
}
160-
161-
if (imp.getVideo() != null) {
162-
return BidType.video;
163-
} else if (imp.getAudio() != null) {
164-
return BidType.audio;
165-
} else if (imp.getXNative() != null) {
166-
return BidType.xNative;
167+
private static BidType getBidType(Bid bid, List<Imp> imps) {
168+
final String impId = bid.getImpid();
169+
for (Imp imp : imps) {
170+
if (imp.getId().equals(impId)) {
171+
if (imp.getBanner() != null) {
172+
return BidType.banner;
173+
} else if (imp.getVideo() != null) {
174+
return BidType.video;
175+
} else if (imp.getAudio() != null) {
176+
return BidType.audio;
177+
}
178+
}
167179
}
168-
return BidType.banner;
180+
throw new PreBidException("Failed to find banner/video/audio impression " + impId);
169181
}
170182
}

src/main/java/org/prebid/server/spring/config/bidder/ResetDigitalConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.prebid.server.bidder.BidderDeps;
44
import org.prebid.server.bidder.resetdigital.ResetDigitalBidder;
5+
import org.prebid.server.currency.CurrencyConversionService;
56
import org.prebid.server.json.JacksonMapper;
67
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
78
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
@@ -30,12 +31,16 @@ BidderConfigurationProperties configurationProperties() {
3031
@Bean
3132
BidderDeps resetDigitalBidderDeps(BidderConfigurationProperties resetDigitalConfigurationProperties,
3233
@NotBlank @Value("${external-url}") String externalUrl,
34+
CurrencyConversionService currencyConversionService,
3335
JacksonMapper mapper) {
3436

3537
return BidderDepsAssembler.forBidder(BIDDER_NAME)
3638
.withConfig(resetDigitalConfigurationProperties)
3739
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38-
.bidderCreator(config -> new ResetDigitalBidder(config.getEndpoint(), mapper))
40+
.bidderCreator(config -> new ResetDigitalBidder(
41+
config.getEndpoint(),
42+
currencyConversionService,
43+
mapper))
3944
.assemble();
4045
}
4146
}
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
adapters:
22
resetdigital:
3-
endpoint: https://prebid.resetdigital.co
4-
endpoint-compression: gzip
3+
endpoint: http://b-us-east14.resetdigital.co:9001
54
meta-info:
65
maintainer-email: biddersupport@resetdigital.co
76
app-media-types:
87
- banner
98
- video
10-
- native
119
- audio
1210
site-media-types:
1311
- banner
1412
- video
15-
- native
1613
- audio
1714
supported-vendors:
1815
vendor-id: 1162
1916
usersync:
2017
cookie-family-name: resetdigital
2118
redirect:
22-
url: https://sync.resetdigital.co/usersync?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}}
23-
support-cors: true
24-
uid-macro: '$UID'
19+
url: https://sync.resetdigital.co/csync?pid=rubicon&redir={{redirect_url}}
20+
support-cors: false
21+
uid-macro: '$USER_ID'

src/main/resources/static/bidder-params/resetdigital.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
"description": "A schema which validates params accepted by the ResetDigital adapter",
55
"type": "object",
66
"properties": {
7-
"placement_id": {
7+
"pubId": {
88
"type": "string",
9-
"description": "Placement ID provided by ResetDigital",
10-
"minLength": 1
9+
"description": "The publisher's ID provided"
10+
},
11+
"zoneId": {
12+
"type": "string",
13+
"description": "Zone ID"
14+
},
15+
"forceBid": {
16+
"type": "boolean",
17+
"description": "Force bids with a test creative"
1118
}
1219
},
13-
"required": ["placement_id"],
14-
"additionalProperties": false
20+
"required": [
21+
"pubId"
22+
]
1523
}

0 commit comments

Comments
 (0)