Skip to content

Commit 8d989c1

Browse files
committed
Merge branch 'refs/heads/master' into zetaglobalssp-bidder-update
2 parents efa5f2b + 6554a1f commit 8d989c1

File tree

143 files changed

+6360
-451
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+6360
-451
lines changed

docs/application-settings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ There are two ways to configure application settings: database and file. This do
1212
- `auction.truncate-target-attr` - Maximum targeting attributes size. Values between 1 and 255.
1313
- `auction.default-integration` - Default integration to assume.
1414
- `auction.debug-allow` - enables debug output in the auction response. Default `true`.
15+
- `auction.impression-limit` - a max number of impressions allowed for the auction, impressions that exceed this limit will be dropped, 0 means no limit.
1516
- `auction.bid-validations.banner-creative-max-size` - Overrides creative max size validation for banners. Valid values
1617
are:
1718
- "skip": don't do anything about creative max size for this publisher

src/main/java/org/prebid/server/auction/requestfactory/AmpRequestFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@ private Future<BidRequest> updateBidRequest(AuctionContext auctionContext) {
414414
.map(bidRequest -> overrideParameters(bidRequest, httpRequest, auctionContext.getPrebidErrors()))
415415
.map(bidRequest -> paramsResolver.resolve(bidRequest, auctionContext, ENDPOINT, true))
416416
.map(bidRequest -> ortb2RequestFactory.removeEmptyEids(bidRequest, auctionContext.getDebugWarnings()))
417+
.compose(resolvedBidRequest -> ortb2RequestFactory.limitImpressions(
418+
account,
419+
resolvedBidRequest,
420+
auctionContext.getDebugWarnings()))
417421
.compose(resolvedBidRequest -> ortb2RequestFactory.validateRequest(
418422
account,
419423
resolvedBidRequest,

src/main/java/org/prebid/server/auction/requestfactory/AuctionRequestFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ private Future<BidRequest> updateAndValidateBidRequest(AuctionContext auctionCon
241241

242242
return storedRequestProcessor.processAuctionRequest(account.getId(), auctionContext.getBidRequest())
243243
.compose(auctionStoredResult -> updateBidRequest(auctionStoredResult, auctionContext))
244+
.compose(bidRequest -> ortb2RequestFactory.limitImpressions(account, bidRequest, debugWarnings))
244245
.compose(bidRequest -> ortb2RequestFactory.validateRequest(
245246
account, bidRequest, httpRequest, auctionContext.getDebugContext(), debugWarnings))
246247
.map(interstitialProcessor::process);

src/main/java/org/prebid/server/auction/requestfactory/Ortb2RequestFactory.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.iab.openrtb.request.Dooh;
77
import com.iab.openrtb.request.Eid;
88
import com.iab.openrtb.request.Geo;
9+
import com.iab.openrtb.request.Imp;
910
import com.iab.openrtb.request.Publisher;
1011
import com.iab.openrtb.request.Regs;
1112
import com.iab.openrtb.request.Site;
@@ -192,6 +193,23 @@ public Future<ActivityInfrastructure> activityInfrastructureFrom(AuctionContext
192193
auctionContext.getDebugContext().getTraceLevel()));
193194
}
194195

196+
public Future<BidRequest> limitImpressions(Account account, BidRequest bidRequest, List<String> warnings) {
197+
final List<Imp> imps = bidRequest.getImp();
198+
final int impsLimit = Optional.ofNullable(account)
199+
.map(Account::getAuction)
200+
.map(AccountAuctionConfig::getImpressionLimit)
201+
.orElse(0);
202+
203+
if (impsLimit > 0 && imps.size() > impsLimit) {
204+
metrics.updateImpsDroppedMetric(imps.size() - impsLimit);
205+
warnings.add(("Only first %d impressions were kept due to the limit, "
206+
+ "all the subsequent impressions have been dropped for the auction").formatted(impsLimit));
207+
return Future.succeededFuture(bidRequest.toBuilder().imp(imps.subList(0, impsLimit)).build());
208+
}
209+
210+
return Future.succeededFuture(bidRequest);
211+
}
212+
195213
public Future<BidRequest> validateRequest(Account account,
196214
BidRequest bidRequest,
197215
HttpRequestContext httpRequestContext,

src/main/java/org/prebid/server/auction/requestfactory/VideoRequestFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ public Future<WithPodErrors<AuctionContext>> fromRequest(RoutingContext routingC
119119

120120
.map(auctionContext -> auctionContext.with(debugResolver.debugContextFrom(auctionContext)))
121121

122+
.compose(auctionContext -> ortb2RequestFactory.limitImpressions(
123+
auctionContext.getAccount(),
124+
auctionContext.getBidRequest(),
125+
auctionContext.getDebugWarnings())
126+
.map(auctionContext::with))
127+
122128
.compose(auctionContext -> ortb2RequestFactory.validateRequest(
123129
auctionContext.getAccount(),
124130
auctionContext.getBidRequest(),

src/main/java/org/prebid/server/bidder/Usersyncer.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import lombok.Value;
44
import org.prebid.server.spring.config.bidder.model.usersync.CookieFamilySource;
55

6+
import java.util.List;
7+
68
@Value(staticConstructor = "of")
79
public class Usersyncer {
810

@@ -16,7 +18,23 @@ public class Usersyncer {
1618

1719
UsersyncMethod redirect;
1820

19-
public static Usersyncer of(String cookieFamilyName, UsersyncMethod iframe, UsersyncMethod redirect) {
20-
return of(true, cookieFamilyName, CookieFamilySource.ROOT, iframe, redirect);
21+
boolean skipWhenInGdprScope;
22+
23+
List<Integer> gppSidToSkip;
24+
25+
public static Usersyncer of(String cookieFamilyName,
26+
UsersyncMethod iframe,
27+
UsersyncMethod redirect,
28+
boolean skipWhenInGdprScope,
29+
List<Integer> gppSidToSkip) {
30+
31+
return of(
32+
true,
33+
cookieFamilyName,
34+
CookieFamilySource.ROOT,
35+
iframe,
36+
redirect,
37+
skipWhenInGdprScope,
38+
gppSidToSkip);
2139
}
2240
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package org.prebid.server.bidder.akcelo;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.fasterxml.jackson.databind.node.ObjectNode;
7+
import com.iab.openrtb.request.BidRequest;
8+
import com.iab.openrtb.request.Imp;
9+
import com.iab.openrtb.request.Publisher;
10+
import com.iab.openrtb.request.Site;
11+
import com.iab.openrtb.response.Bid;
12+
import com.iab.openrtb.response.BidResponse;
13+
import com.iab.openrtb.response.SeatBid;
14+
import org.apache.commons.collections4.CollectionUtils;
15+
import org.prebid.server.bidder.Bidder;
16+
import org.prebid.server.bidder.model.BidderBid;
17+
import org.prebid.server.bidder.model.BidderCall;
18+
import org.prebid.server.bidder.model.BidderError;
19+
import org.prebid.server.bidder.model.HttpRequest;
20+
import org.prebid.server.bidder.model.Result;
21+
import org.prebid.server.exception.PreBidException;
22+
import org.prebid.server.json.DecodeException;
23+
import org.prebid.server.json.JacksonMapper;
24+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
25+
import org.prebid.server.proto.openrtb.ext.request.ExtPublisher;
26+
import org.prebid.server.proto.openrtb.ext.request.ExtPublisherPrebid;
27+
import org.prebid.server.proto.openrtb.ext.request.akcelo.ExtImpAkcelo;
28+
import org.prebid.server.proto.openrtb.ext.response.BidType;
29+
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
30+
import org.prebid.server.util.BidderUtil;
31+
import org.prebid.server.util.HttpUtil;
32+
33+
import java.util.ArrayList;
34+
import java.util.Collection;
35+
import java.util.Collections;
36+
import java.util.List;
37+
import java.util.Objects;
38+
import java.util.Optional;
39+
40+
public class AkceloBidder implements Bidder<BidRequest> {
41+
42+
private static final TypeReference<ExtPrebid<?, ExtImpAkcelo>> AKCELO_EXT_TYPE_REFERENCE =
43+
new TypeReference<>() {
44+
};
45+
private static final String BIDDER_NAME = "akcelo";
46+
47+
private final String endpointUrl;
48+
private final JacksonMapper mapper;
49+
50+
public AkceloBidder(String endpointUrl, JacksonMapper mapper) {
51+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
52+
this.mapper = Objects.requireNonNull(mapper);
53+
}
54+
55+
@Override
56+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
57+
final List<Imp> imps = request.getImp();
58+
final List<Imp> modifiedImps = new ArrayList<>();
59+
60+
final ExtImpAkcelo firstExtImp;
61+
try {
62+
firstExtImp = parseImpExt(imps.getFirst());
63+
} catch (PreBidException e) {
64+
return Result.withError(BidderError.badInput(e.getMessage()));
65+
}
66+
67+
for (final Imp imp : imps) {
68+
modifiedImps.add(modifyImp(imp));
69+
}
70+
71+
final BidRequest outgoingRequest = modifyRequest(request, modifiedImps, firstExtImp.getSiteId());
72+
return Result.withValue(BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper));
73+
}
74+
75+
private ExtImpAkcelo parseImpExt(Imp imp) {
76+
try {
77+
return mapper.mapper().convertValue(imp.getExt(), AKCELO_EXT_TYPE_REFERENCE).getBidder();
78+
} catch (IllegalArgumentException e) {
79+
throw new PreBidException(e.getMessage());
80+
}
81+
}
82+
83+
private Imp modifyImp(Imp imp) {
84+
return imp.toBuilder()
85+
.ext(mapper.mapper().createObjectNode().set(BIDDER_NAME, imp.getExt().get("bidder")))
86+
.build();
87+
}
88+
89+
private BidRequest modifyRequest(BidRequest request, List<Imp> imps, String siteId) {
90+
return request.toBuilder()
91+
.imp(imps)
92+
.site(modifySite(request.getSite(), siteId))
93+
.build();
94+
}
95+
96+
private Site modifySite(Site site, String siteId) {
97+
final Publisher publisher = Optional.ofNullable(site)
98+
.map(Site::getPublisher)
99+
.map(Publisher::toBuilder)
100+
.orElseGet(Publisher::builder)
101+
.ext(ExtPublisher.of(ExtPublisherPrebid.of(siteId)))
102+
.build();
103+
104+
return Optional.ofNullable(site)
105+
.map(Site::toBuilder)
106+
.orElseGet(Site::builder)
107+
.publisher(publisher)
108+
.build();
109+
}
110+
111+
@Override
112+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
113+
try {
114+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
115+
final List<BidderError> errors = new ArrayList<>();
116+
return Result.of(extractBids(bidResponse, errors), errors);
117+
} catch (DecodeException e) {
118+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
119+
}
120+
}
121+
122+
private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
123+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
124+
return Collections.emptyList();
125+
}
126+
127+
return bidResponse.getSeatbid().stream()
128+
.filter(Objects::nonNull)
129+
.map(SeatBid::getBid)
130+
.filter(Objects::nonNull)
131+
.flatMap(Collection::stream)
132+
.filter(Objects::nonNull)
133+
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
134+
.filter(Objects::nonNull)
135+
.toList();
136+
}
137+
138+
private BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
139+
final BidType bidType = getBidType(bid, errors);
140+
return bidType == null ? null : BidderBid.of(bid, bidType, currency);
141+
}
142+
143+
private BidType getBidType(Bid bid, List<BidderError> errors) {
144+
final Integer mType = bid.getMtype();
145+
if (mType != null) {
146+
return switch (mType) {
147+
case 1 -> BidType.banner;
148+
case 2 -> BidType.video;
149+
case 4 -> BidType.xNative;
150+
default -> {
151+
errors.add(BidderError.badServerResponse("unable to get media type " + mType));
152+
yield null;
153+
}
154+
};
155+
}
156+
157+
return getExtBidPrebidType(bid, errors);
158+
}
159+
160+
private BidType getExtBidPrebidType(Bid bid, List<BidderError> errors) {
161+
return Optional.ofNullable(bid.getExt())
162+
.map(ext -> ext.get("prebid"))
163+
.filter(JsonNode::isObject)
164+
.map(ObjectNode.class::cast)
165+
.map(this::parseExtBidPrebid)
166+
.map(ExtBidPrebid::getType)
167+
.orElseGet(() -> {
168+
errors.add(BidderError.badServerResponse("missing media type for bid " + bid.getId()));
169+
return null;
170+
});
171+
}
172+
173+
private ExtBidPrebid parseExtBidPrebid(ObjectNode prebid) {
174+
try {
175+
return mapper.mapper().treeToValue(prebid, ExtBidPrebid.class);
176+
} catch (JsonProcessingException e) {
177+
return null;
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)