From 02ab3792370f46194e797f570697f6a748728fb5 Mon Sep 17 00:00:00 2001 From: Alex Maltsev Date: Tue, 28 Jan 2025 18:09:26 +0200 Subject: [PATCH] Core: Transition PAAPI parameters (#3670) --- .../server/auction/BidResponseCreator.java | 201 +++++- .../server/auction/ExchangeService.java | 2 +- .../prebid/server/auction/ImpAdjuster.java | 29 + .../auction/model/BidRejectionTracker.java | 11 - .../server/auction/model/PaaFormat.java | 12 + .../server/bidder/HttpBidderRequester.java | 12 +- .../bidder/consumable/ConsumableBidder.java | 9 +- .../server/bidder/criteo/CriteoBidder.java | 28 +- .../bidder/criteo/CriteoExtBidResponse.java | 3 +- .../criteo/CriteoIgiExtBidResponse.java | 13 - .../criteo/CriteoIgsIgiExtBidResponse.java | 10 - .../org/prebid/server/bidder/ix/IxBidder.java | 23 +- .../bidder/medianet/MedianetBidder.java | 18 +- .../server/bidder/model/BidderSeatBid.java | 5 + .../bidder/model/BidderSeatBidInfo.java | 16 +- .../bidder/model/CompositeBidderResponse.java | 12 +- .../server/bidder/openx/OpenxBidder.java | 21 +- .../bidder/pubmatic/PubmaticBidder.java | 22 +- .../prebid/server/handler/SetuidHandler.java | 1 + .../openrtb/ext/request/ExtRequestPrebid.java | 6 + .../openrtb/ext/response/ExtBidResponse.java | 5 + .../proto/openrtb/ext/response/ExtIgi.java | 20 + .../proto/openrtb/ext/response/ExtIgiIgb.java | 22 + .../proto/openrtb/ext/response/ExtIgiIgs.java | 18 + .../openrtb/ext/response/ExtIgiIgsExt.java | 11 + .../settings/model/AccountAuctionConfig.java | 4 + .../spring/config/ServiceConfiguration.java | 4 + .../functional/model/bidder/BidderName.groovy | 1 + .../model/config/AccountAuctionConfig.groovy | 2 + .../request/auction/AuctionEnvironment.groovy | 19 + .../model/request/auction/Bidder.groovy | 2 + .../model/request/auction/ImpExt.groovy | 5 +- .../InterestGroupAuctionSupport.groovy | 11 + .../model/request/auction/PaaFormat.groovy | 13 + .../model/request/auction/Prebid.groovy | 1 + .../response/auction/BidResponseExt.groovy | 3 + .../auction/InterestGroupAuctionBuyer.groovy | 17 + .../InterestGroupAuctionBuyerExt.groovy | 10 + .../auction/InterestGroupAuctionIntent.groovy | 18 + .../InterestGroupAuctionIntentExt.groovy | 11 + .../auction/InterestGroupAuctionSeller.groovy | 14 + .../InterestGroupAuctionSellerExt.groovy | 10 + .../response/auction/OpenxBidResponse.groovy | 1 - .../functional/tests/BidderParamsSpec.groovy | 72 +- .../tests/bidder/openx/OpenxSpec.groovy | 450 +++++++++++- .../auction/BidResponseCreatorTest.java | 663 +++++++++++++++--- .../server/auction/ExchangeServiceTest.java | 37 +- .../server/auction/ImpAdjusterTest.java | 74 ++ .../bidder/HttpBidderRequesterTest.java | 71 +- .../bidder/criteo/CriteoBidderTest.java | 40 +- .../prebid/server/bidder/ix/IxBidderTest.java | 18 +- .../bidder/medianet/MedianetBidderTest.java | 25 +- .../server/bidder/openx/OpenxBidderTest.java | 29 +- .../bidder/pubmatic/PubmaticBidderTest.java | 15 +- .../openrtb2/ix/test-auction-ix-response.json | 42 +- .../it/openrtb2/ix/test-ix-bid-request.json | 5 + .../it/openrtb2/ix/test-ix-bid-response.json | 40 +- 57 files changed, 1847 insertions(+), 410 deletions(-) create mode 100644 src/main/java/org/prebid/server/auction/model/PaaFormat.java delete mode 100644 src/main/java/org/prebid/server/bidder/criteo/CriteoIgiExtBidResponse.java delete mode 100644 src/main/java/org/prebid/server/bidder/criteo/CriteoIgsIgiExtBidResponse.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgi.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgb.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgs.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgsExt.java create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy create mode 100644 src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy diff --git a/src/main/java/org/prebid/server/auction/BidResponseCreator.java b/src/main/java/org/prebid/server/auction/BidResponseCreator.java index 86fe28fdcbe..4242281a5dd 100644 --- a/src/main/java/org/prebid/server/auction/BidResponseCreator.java +++ b/src/main/java/org/prebid/server/auction/BidResponseCreator.java @@ -34,6 +34,7 @@ import org.prebid.server.auction.model.CachedDebugLog; import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; +import org.prebid.server.auction.model.PaaFormat; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.debug.DebugContext; import org.prebid.server.auction.requestfactory.Ortb2ImplicitParametersResolver; @@ -61,6 +62,11 @@ import org.prebid.server.identity.IdGeneratorType; import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; +import org.prebid.server.log.ConditionalLogger; +import org.prebid.server.log.Logger; +import org.prebid.server.log.LoggerFactory; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.request.ExtImp; import org.prebid.server.proto.openrtb.ext.request.ExtImpAuctionEnvironment; import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid; @@ -82,6 +88,10 @@ import org.prebid.server.proto.openrtb.ext.response.ExtBidderError; import org.prebid.server.proto.openrtb.ext.response.ExtDebugTrace; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgb; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgsExt; import org.prebid.server.proto.openrtb.ext.response.ExtResponseCache; import org.prebid.server.proto.openrtb.ext.response.ExtResponseDebug; import org.prebid.server.proto.openrtb.ext.response.ExtTraceActivityInfrastructure; @@ -118,6 +128,9 @@ public class BidResponseCreator { + private static final Logger logger = LoggerFactory.getLogger(BidResponseCreator.class); + private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); + private static final String CACHE = "cache"; private static final String PREBID_EXT = "prebid"; private static final Integer DEFAULT_BID_LIMIT_MIN = 1; @@ -127,6 +140,7 @@ public class BidResponseCreator { private static final String TARGETING_ENV_APP_VALUE = "mobile-app"; private static final String TARGETING_ENV_AMP_VALUE = "amp"; + private final double logSamplingRate; private final CoreCacheService coreCacheService; private final BidderCatalog bidderCatalog; private final VastModifier vastModifier; @@ -139,6 +153,7 @@ public class BidResponseCreator { private final int truncateAttrChars; private final Clock clock; private final JacksonMapper mapper; + private final Metrics metrics; private final CacheTtl mediaTypeCacheTtl; private final CacheDefaultTtlProperties cacheDefaultProperties; @@ -146,7 +161,8 @@ public class BidResponseCreator { private final String cachePath; private final String cacheAssetUrlTemplate; - public BidResponseCreator(CoreCacheService coreCacheService, + public BidResponseCreator(double logSamplingRate, + CoreCacheService coreCacheService, BidderCatalog bidderCatalog, VastModifier vastModifier, EventsService eventsService, @@ -158,6 +174,7 @@ public BidResponseCreator(CoreCacheService coreCacheService, int truncateAttrChars, Clock clock, JacksonMapper mapper, + Metrics metrics, CacheTtl mediaTypeCacheTtl, CacheDefaultTtlProperties cacheDefaultProperties) { @@ -175,10 +192,13 @@ public BidResponseCreator(CoreCacheService coreCacheService, this.mapper = Objects.requireNonNull(mapper); this.mediaTypeCacheTtl = Objects.requireNonNull(mediaTypeCacheTtl); this.cacheDefaultProperties = Objects.requireNonNull(cacheDefaultProperties); + this.metrics = Objects.requireNonNull(metrics); + + this.logSamplingRate = logSamplingRate; + cacheAssetUrlTemplate = Objects.requireNonNull(coreCacheService.getCachedAssetURLTemplate()); cacheHost = Objects.requireNonNull(coreCacheService.getEndpointHost()); cachePath = Objects.requireNonNull(coreCacheService.getEndpointPath()); - cacheAssetUrlTemplate = Objects.requireNonNull(coreCacheService.getCachedAssetURLTemplate()); } private static int validateTruncateAttrChars(int truncateAttrChars) { @@ -209,16 +229,19 @@ Future createOnSkippedAuction(AuctionContext auctionContext, List create(AuctionContext auctionContext, BidRequestCacheInfo cacheInfo, + BidderAliases aliases, Map bidderToMultiBids) { return videoStoredDataResult(auctionContext) - .compose(videoStoredData -> create(videoStoredData, auctionContext, cacheInfo, bidderToMultiBids)) + .compose(videoStoredData -> + create(videoStoredData, auctionContext, cacheInfo, aliases, bidderToMultiBids)) .map(bidResponse -> populateSeatNonBid(auctionContext, bidResponse)); } private Future create(VideoStoredDataResult videoStoredDataResult, AuctionContext auctionContext, BidRequestCacheInfo cacheInfo, + BidderAliases aliases, Map bidderToMultiBids) { final EventsContext eventsContext = createEventsContext(auctionContext); @@ -236,6 +259,7 @@ private Future create(VideoStoredDataResult videoStoredDataResult, toBidderResponseInfos(categoryMappingResult, cacheInfo, auctionContext), auctionContext, cacheInfo, + aliases, bidderToMultiBids, videoStoredDataResult, eventsContext)); @@ -418,7 +442,8 @@ private List toBidderResponseInfos(CategoryMappingResult cat seatBid.getHttpCalls(), seatBid.getErrors(), seatBid.getWarnings(), - seatBid.getFledgeAuctionConfigs()); + seatBid.getFledgeAuctionConfigs(), + seatBid.getIgi()); result.add(BidderResponseInfo.of(bidder, bidderSeatBidInfo, bidderResponse.getResponseTime())); } @@ -553,6 +578,7 @@ private static CategoryMappingResult addCategoryMappingErrors(CategoryMappingRes private Future cacheBidsAndCreateResponse(List bidderResponses, AuctionContext auctionContext, BidRequestCacheInfo cacheInfo, + BidderAliases aliases, Map bidderToMultiBids, VideoStoredDataResult videoStoredDataResult, EventsContext eventsContext) { @@ -563,6 +589,7 @@ private Future cacheBidsAndCreateResponse(List final ExtBidResponse extBidResponse = toExtBidResponse( bidderResponses, auctionContext, + aliases, CacheServiceResult.empty(), VideoStoredDataResult.empty(), eventsContext.getAuctionTimestamp(), @@ -606,6 +633,7 @@ private Future cacheBidsAndCreateResponse(List .map(cacheResult -> toBidResponse( bidderResponseInfos, auctionContext, + aliases, targeting, cacheInfo, cacheResult, @@ -760,6 +788,7 @@ private static List injectTargeting(List bidderImpIdBidInfos, */ private ExtBidResponse toExtBidResponse(List bidderResponseInfos, AuctionContext auctionContext, + BidderAliases aliases, CacheServiceResult cacheResult, VideoStoredDataResult videoStoredDataResult, long auctionTimestamp, @@ -769,6 +798,10 @@ private ExtBidResponse toExtBidResponse(List bidderResponseI final DebugContext debugContext = auctionContext.getDebugContext(); final boolean debugEnabled = debugContext.isDebugEnabled(); + final PaaResult paaResult = toPaaOutput(bidderResponseInfos, auctionContext, aliases); + final List igi = paaResult.igis(); + final ExtBidResponseFledge fledge = paaResult.fledge(); + final ExtResponseDebug extResponseDebug = toExtResponseDebug( bidderResponseInfos, auctionContext, cacheResult, debugEnabled); final Map> errors = toExtBidderErrors( @@ -778,9 +811,8 @@ private ExtBidResponse toExtBidResponse(List bidderResponseI final Map responseTimeMillis = toResponseTimes(bidderResponseInfos, cacheResult); - final ExtBidResponseFledge extBidResponseFledge = toExtBidResponseFledge(bidderResponseInfos, auctionContext); final ExtBidResponsePrebid prebid = toExtBidResponsePrebid( - auctionTimestamp, auctionContext.getBidRequest(), extBidResponseFledge); + auctionTimestamp, auctionContext.getBidRequest(), fledge); return ExtBidResponse.builder() .debug(extResponseDebug) @@ -788,6 +820,7 @@ private ExtBidResponse toExtBidResponse(List bidderResponseI .warnings(warnings) .responsetimemillis(responseTimeMillis) .tmaxrequest(auctionContext.getBidRequest().getTmax()) + .igi(igi) .prebid(prebid) .build(); } @@ -809,22 +842,139 @@ private ExtBidResponsePrebid toExtBidResponsePrebid(long auctionTimestamp, .build(); } - private ExtBidResponseFledge toExtBidResponseFledge(List bidderResponseInfos, - AuctionContext auctionContext) { + private PaaResult toPaaOutput(List bidderResponseInfos, + AuctionContext auctionContext, + BidderAliases aliases) { + + final PaaFormat paaFormat = resolvePaaFormat(auctionContext); + final List igis = extractIgis(bidderResponseInfos, auctionContext, aliases); + final List extIgi = paaFormat == PaaFormat.IAB && !igis.isEmpty() ? igis : null; + final List fledgeConfigs = paaFormat == PaaFormat.ORIGINAL + ? toOriginalFledgeFormat(igis) + : Collections.emptyList(); + + // TODO: Remove after transition period final List imps = auctionContext.getBidRequest().getImp(); - final List fledgeConfigs = bidderResponseInfos.stream() - .flatMap(bidderResponseInfo -> fledgeConfigsForBidder(bidderResponseInfo, imps)) + final List deprecatedFledgeConfigs = bidderResponseInfos.stream() + .flatMap(bidderResponseInfo -> toDeprecatedFledgeConfigs(bidderResponseInfo, aliases, imps)) + .toList(); + + final List combinedFledgeConfigs = ListUtils.union(deprecatedFledgeConfigs, fledgeConfigs); + final ExtBidResponseFledge extBidResponseFledge = combinedFledgeConfigs.isEmpty() + ? null + : ExtBidResponseFledge.of(combinedFledgeConfigs); + + return new PaaResult(extIgi, extBidResponseFledge); + } + + private List extractIgis(List bidderResponseInfos, + AuctionContext auctionContext, + BidderAliases aliases) { + + return bidderResponseInfos.stream() + .flatMap(responseInfo -> responseInfo.getSeatBid().getIgi().stream() + .map(igi -> prepareExtIgi(igi, responseInfo.getBidder(), auctionContext, aliases))) + .filter(Objects::nonNull) .toList(); - return !fledgeConfigs.isEmpty() ? ExtBidResponseFledge.of(fledgeConfigs) : null; } - private Stream fledgeConfigsForBidder(BidderResponseInfo bidderResponseInfo, List imps) { + private ExtIgi prepareExtIgi(ExtIgi igi, String bidder, AuctionContext auctionContext, BidderAliases aliases) { + if (igi == null) { + return null; + } + + final boolean shouldDropIgb = StringUtils.isEmpty(igi.getImpid()) && CollectionUtils.isNotEmpty(igi.getIgb()); + if (shouldDropIgb) { + final String warning = "ExtIgi with absent impId from bidder: " + bidder; + if (auctionContext.getDebugContext().isDebugEnabled()) { + auctionContext.getDebugWarnings().add(warning); + } + conditionalLogger.warn(warning, logSamplingRate); + metrics.updateAlertsMetrics(MetricName.general); + } + + final List updatedIgs = prepareExtIgiIgs(igi.getIgs(), bidder, auctionContext, aliases); + final List preparedIgs = updatedIgs.isEmpty() ? null : updatedIgs; + final List preparedIgb = shouldDropIgb ? null : igi.getIgb(); + + return ObjectUtils.anyNotNull(preparedIgs, preparedIgb) + ? igi.toBuilder().igs(preparedIgs).igb(preparedIgb).build() + : null; + } + + private List prepareExtIgiIgs(List igiIgs, + String bidder, + AuctionContext auctionContext, + BidderAliases aliases) { + + if (igiIgs == null) { + return Collections.emptyList(); + } + + final boolean debugEnabled = auctionContext.getDebugContext().isDebugEnabled(); + final List preparedIgiIgs = new ArrayList<>(); + for (ExtIgiIgs extIgiIgs : igiIgs) { + if (extIgiIgs == null) { + continue; + } + + if (StringUtils.isEmpty(extIgiIgs.getImpId())) { + final String warning = "ExtIgiIgs with absent impId from bidder: " + bidder; + if (debugEnabled) { + auctionContext.getDebugWarnings().add(warning); + } + conditionalLogger.warn(warning, logSamplingRate); + metrics.updateAlertsMetrics(MetricName.general); + continue; + } + + if (extIgiIgs.getConfig() == null) { + final String warning = "ExtIgiIgs with absent config from bidder: " + bidder; + if (debugEnabled) { + auctionContext.getDebugWarnings().add(warning); + } + conditionalLogger.warn(warning, logSamplingRate); + metrics.updateAlertsMetrics(MetricName.general); + continue; + } + + final ExtIgiIgs preparedExtIgiIgs = extIgiIgs.toBuilder() + .ext(ExtIgiIgsExt.of(bidder, bidderCatalog.resolveBaseBidder(aliases.resolveBidder(bidder)))) + .build(); + + preparedIgiIgs.add(preparedExtIgiIgs); + } + + return preparedIgiIgs; + } + + private List toOriginalFledgeFormat(List igis) { + return igis.stream() + .map(ExtIgi::getIgs) + .flatMap(Collection::stream) + .map(BidResponseCreator::extIgiIgsToFledgeConfig) + .toList(); + } + + private static FledgeAuctionConfig extIgiIgsToFledgeConfig(ExtIgiIgs extIgiIgs) { + return FledgeAuctionConfig.builder() + .bidder(extIgiIgs.getExt().getBidder()) + .adapter(extIgiIgs.getExt().getAdapter()) + .impId(extIgiIgs.getImpId()) + .config(extIgiIgs.getConfig()) + .build(); + } + + private Stream toDeprecatedFledgeConfigs(BidderResponseInfo bidderResponseInfo, + BidderAliases aliases, + List imps) { + return Optional.ofNullable(bidderResponseInfo.getSeatBid().getFledgeAuctionConfigs()) .stream() .flatMap(Collection::stream) .filter(fledgeConfig -> validateFledgeConfig(fledgeConfig, imps)) - .map(fledgeConfig -> fledgeConfigWithBidder(fledgeConfig, bidderResponseInfo.getBidder())); + .map(fledgeConfig -> fledgeConfigWithBidder(fledgeConfig, bidderResponseInfo.getBidder(), aliases)); } private boolean validateFledgeConfig(FledgeAuctionConfig fledgeAuctionConfig, List imps) { @@ -836,10 +986,13 @@ private boolean validateFledgeConfig(FledgeAuctionConfig fledgeAuctionConfig, Li return fledgeEnabled == ExtImpAuctionEnvironment.ON_DEVICE_IG_AUCTION_FLEDGE; } - private static FledgeAuctionConfig fledgeConfigWithBidder(FledgeAuctionConfig fledgeConfig, String bidderName) { + private FledgeAuctionConfig fledgeConfigWithBidder(FledgeAuctionConfig fledgeConfig, + String bidder, + BidderAliases aliases) { + return fledgeConfig.toBuilder() - .bidder(bidderName) - .adapter(bidderName) + .bidder(bidder) + .adapter(bidderCatalog.resolveBaseBidder(aliases.resolveBidder(bidder))) .build(); } @@ -1154,11 +1307,23 @@ private static Map toResponseTimes(Collection Optional.ofNullable(auctionContext.getAccount()) + .map(Account::getAuction) + .map(AccountAuctionConfig::getPaaFormat)) + .orElse(PaaFormat.ORIGINAL); + } + /** * Returns {@link BidResponse} based on list of {@link BidderResponse}s and {@link CacheServiceResult}. */ private BidResponse toBidResponse(List bidderResponseInfos, AuctionContext auctionContext, + BidderAliases aliases, ExtRequestTargeting targeting, BidRequestCacheInfo requestCacheInfo, CacheServiceResult cacheResult, @@ -1189,6 +1354,7 @@ private BidResponse toBidResponse(List bidderResponseInfos, final ExtBidResponse extBidResponse = toExtBidResponse( bidderResponseInfos, auctionContext, + aliases, cacheResult, videoStoredDataResult, auctionTimestamp, @@ -1800,4 +1966,7 @@ private T convertValue(JsonNode jsonNode, String key, Class typeClass) { return null; } } + + private record PaaResult(List igis, ExtBidResponseFledge fledge) { + } } diff --git a/src/main/java/org/prebid/server/auction/ExchangeService.java b/src/main/java/org/prebid/server/auction/ExchangeService.java index 319dd51084f..56cd8192501 100644 --- a/src/main/java/org/prebid/server/auction/ExchangeService.java +++ b/src/main/java/org/prebid/server/auction/ExchangeService.java @@ -273,7 +273,7 @@ private Future runAuction(AuctionContext receivedContext) { .map(auctionParticipations -> updateResponsesMetrics(auctionParticipations, account, aliases)) .map(context::with)) // produce response from bidder results - .compose(context -> bidResponseCreator.create(context, cacheInfo, bidderToMultiBid) + .compose(context -> bidResponseCreator.create(context, cacheInfo, aliases, bidderToMultiBid) .map(bidResponse -> criteriaLogManager.traceResponse( logger, bidResponse, diff --git a/src/main/java/org/prebid/server/auction/ImpAdjuster.java b/src/main/java/org/prebid/server/auction/ImpAdjuster.java index 86e581b06e8..26cbee95358 100644 --- a/src/main/java/org/prebid/server/auction/ImpAdjuster.java +++ b/src/main/java/org/prebid/server/auction/ImpAdjuster.java @@ -1,10 +1,13 @@ package org.prebid.server.auction; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Imp; import org.prebid.server.json.JacksonMapper; import org.prebid.server.json.JsonMerger; +import org.prebid.server.util.StreamUtil; import org.prebid.server.validation.ImpValidator; import java.util.Iterator; @@ -15,6 +18,8 @@ public class ImpAdjuster { private static final String IMP_EXT = "ext"; + private static final String EXT_AE = "ae"; + private static final String EXT_IGS = "igs"; private static final String EXT_PREBID = "prebid"; private static final String EXT_PREBID_BIDDER = "bidder"; private static final String EXT_PREBID_IMP = "imp"; @@ -33,6 +38,8 @@ public ImpAdjuster(JacksonMapper jacksonMapper, } public Imp adjust(Imp originalImp, String bidder, BidderAliases bidderAliases, List debugMessages) { + setAeParams(originalImp.getExt()); + final JsonNode impExtPrebidImp = bidderParamsFromImpExtPrebidImp(originalImp.getExt()); if (impExtPrebidImp == null) { return originalImp; @@ -65,6 +72,28 @@ public Imp adjust(Imp originalImp, String bidder, BidderAliases bidderAliases, L } } + private void setAeParams(ObjectNode ext) { + final int extAe = Optional.ofNullable(ext) + .map(extNode -> extNode.get(EXT_AE)) + .filter(JsonNode::isInt) + .map(JsonNode::asInt) + .orElse(-1); + + final boolean extIgsAePresent = Optional.ofNullable(ext) + .map(extNode -> extNode.get(EXT_IGS)) + .filter(JsonNode::isArray) + .stream() + .flatMap(extNode -> StreamUtil.asStream(extNode.spliterator())) + .filter(Objects::nonNull) + .anyMatch(igsElementNode -> igsElementNode.has(EXT_AE)); + + if (!extIgsAePresent && (extAe == 0 || extAe == 1)) { + final ArrayNode extIgs = jacksonMapper.mapper().createArrayNode(); + extIgs.add(jacksonMapper.mapper().createObjectNode().set(EXT_AE, IntNode.valueOf(extAe))); + ext.set(EXT_IGS, extIgs); + } + } + private static JsonNode bidderParamsFromImpExtPrebidImp(ObjectNode ext) { return Optional.ofNullable(ext) .map(extNode -> extNode.get(EXT_PREBID)) diff --git a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java index 9810606ada3..476e61fac09 100644 --- a/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java +++ b/src/main/java/org/prebid/server/auction/model/BidRejectionTracker.java @@ -47,10 +47,6 @@ public BidRejectionTracker(String bidder, Set involvedImpIds, double log rejectedBids = new HashMap<>(); } - /** - * Restores ONLY imps from rejection, rejected bids are preserved for analytics. - * A bid can be rejected only once. - */ public void succeed(Collection bids) { bids.stream() .map(BidderBid::getBid) @@ -124,10 +120,6 @@ public void rejectAllImps(BidRejectionReason reason) { involvedImpIds.forEach(impId -> rejectImp(impId, reason)); } - /** - * If an impression has at least one valid bid, it's not considered rejected. - * If no valid bids are returned for the impression, only the first one rejected reason will be returned - */ public Map getRejectedImps() { final Map rejectedImpIds = new HashMap<>(); for (String impId : involvedImpIds) { @@ -144,9 +136,6 @@ public Map getRejectedImps() { return rejectedImpIds; } - /** - * Bid is absent for the non-bid code from 0 to 299 - */ public Map>> getRejectedBids() { final Map>> missingImpIds = new HashMap<>(); for (String impId : involvedImpIds) { diff --git a/src/main/java/org/prebid/server/auction/model/PaaFormat.java b/src/main/java/org/prebid/server/auction/model/PaaFormat.java new file mode 100644 index 00000000000..8367b61c0f2 --- /dev/null +++ b/src/main/java/org/prebid/server/auction/model/PaaFormat.java @@ -0,0 +1,12 @@ +package org.prebid.server.auction.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum PaaFormat { + + @JsonProperty("original") + ORIGINAL, + + @JsonProperty("iab") + IAB +} diff --git a/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java b/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java index 0345aa4c29e..3a24683c774 100644 --- a/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java +++ b/src/main/java/org/prebid/server/bidder/HttpBidderRequester.java @@ -31,6 +31,7 @@ import org.prebid.server.log.LoggerFactory; import org.prebid.server.model.CaseInsensitiveMultiMap; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; import org.prebid.server.util.HttpUtil; import org.prebid.server.vertx.httpclient.HttpClient; @@ -248,7 +249,7 @@ private static byte[] gzip(byte[] value) { */ private Future> failResponse(Throwable exception, HttpRequest httpRequest) { conditionalLogger.warn("Error occurred while sending HTTP request to a bidder url: %s with message: %s" - .formatted(httpRequest.getUri(), exception.getMessage()), logSamplingRate); + .formatted(httpRequest.getUri(), exception.getMessage()), logSamplingRate); logger.debug("Error occurred while sending HTTP request to a bidder url: {}", exception, httpRequest.getUri()); @@ -346,6 +347,7 @@ private static class ResultBuilder { private final Map, BidderCall> bidderCallsRecorded = new HashMap<>(); private final List bidsRecorded = new ArrayList<>(); private final List errorsRecorded = new ArrayList<>(); + private final List igiRecorded = new ArrayList<>(); private final List fledgeRecorded = new ArrayList<>(); ResultBuilder(List> httpRequests, @@ -366,6 +368,7 @@ void addHttpCall(BidderCall bidderCall, CompositeBidderResponse bidderRespons handleBids(bidderResponse); handleBidderErrors(bidderResponse); handleBidderCallError(bidderCall); + handleIgis(bidderResponse); handleFledgeAuctionConfigs(bidderResponse); } @@ -429,6 +432,12 @@ private void handleFledgeAuctionConfigs(CompositeBidderResponse bidderResponse) .ifPresent(fledgeRecorded::addAll); } + private void handleIgis(CompositeBidderResponse bidderResponse) { + Optional.ofNullable(bidderResponse) + .map(CompositeBidderResponse::getIgi) + .ifPresent(igiRecorded::addAll); + } + BidderSeatBid toBidderSeatBid(boolean debugEnabled) { final List> httpCalls = new ArrayList<>(bidderCallsRecorded.values()); httpRequests.stream() @@ -446,6 +455,7 @@ BidderSeatBid toBidderSeatBid(boolean debugEnabled) { .bids(bidsRecorded) .httpCalls(extHttpCalls) .errors(errors) + .igi(igiRecorded) .fledgeAuctionConfigs(fledgeRecorded) .build(); } diff --git a/src/main/java/org/prebid/server/bidder/consumable/ConsumableBidder.java b/src/main/java/org/prebid/server/bidder/consumable/ConsumableBidder.java index 344680b9aa4..5b70032a98d 100644 --- a/src/main/java/org/prebid/server/bidder/consumable/ConsumableBidder.java +++ b/src/main/java/org/prebid/server/bidder/consumable/ConsumableBidder.java @@ -121,9 +121,12 @@ public Result> makeBids(BidderCall httpCall, BidRequ public CompositeBidderResponse makeBidderResponse(BidderCall httpCall, BidRequest bidRequest) { try { final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); - final List bidderErrors = new ArrayList<>(); - return CompositeBidderResponse.builder().bids(extractConsumableBids(bidRequest, bidResponse, bidderErrors)) - .errors(bidderErrors).build(); + final List errors = new ArrayList<>(); + + return CompositeBidderResponse.builder() + .bids(extractConsumableBids(bidRequest, bidResponse, errors)) + .errors(errors) + .build(); } catch (DecodeException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); } diff --git a/src/main/java/org/prebid/server/bidder/criteo/CriteoBidder.java b/src/main/java/org/prebid/server/bidder/criteo/CriteoBidder.java index 064bac48c48..9bf68e89e24 100644 --- a/src/main/java/org/prebid/server/bidder/criteo/CriteoBidder.java +++ b/src/main/java/org/prebid/server/bidder/criteo/CriteoBidder.java @@ -19,7 +19,7 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidMeta; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; @@ -31,7 +31,6 @@ public class CriteoBidder implements Bidder { - private static final String BIDDER_NAME = "criteo"; private final String endpointUrl; private final JacksonMapper mapper; @@ -55,9 +54,12 @@ public Result> makeBids(BidderCall httpCall, BidRequ public CompositeBidderResponse makeBidderResponse(BidderCall httpCall, BidRequest bidRequest) { try { final CriteoBidResponse bidResponse = mapper.decodeValue( - httpCall.getResponse().getBody(), - CriteoBidResponse.class); - return CompositeBidderResponse.withBids(extractBids(bidResponse), extractFledge(bidResponse)); + httpCall.getResponse().getBody(), CriteoBidResponse.class); + + return CompositeBidderResponse.builder() + .bids(extractBids(bidResponse)) + .igi(extractIgi(bidResponse)) + .build(); } catch (DecodeException | PreBidException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); } @@ -105,21 +107,11 @@ private ObjectNode makeExt(String networkName) { .build()); } - private static List extractFledge(CriteoBidResponse bidResponse) { - final List fledgeConfigs = Optional.ofNullable(bidResponse) + private static List extractIgi(CriteoBidResponse bidResponse) { + return Optional.ofNullable(bidResponse) .map(CriteoBidResponse::getExt) .map(CriteoExtBidResponse::getIgi) .filter(CollectionUtils::isNotEmpty) - .orElse(Collections.emptyList()) - .stream() - .filter(igi -> CollectionUtils.isNotEmpty(igi.getIgs()) && igi.getIgs().getFirst() != null) - .map(igi -> FledgeAuctionConfig.builder() - .impId(igi.getImpId()) - .bidder(BIDDER_NAME) - .config(igi.getIgs().getFirst().getConfig()) - .build()) - .toList(); - - return CollectionUtils.isEmpty(fledgeConfigs) ? null : fledgeConfigs; + .orElse(Collections.emptyList()); } } diff --git a/src/main/java/org/prebid/server/bidder/criteo/CriteoExtBidResponse.java b/src/main/java/org/prebid/server/bidder/criteo/CriteoExtBidResponse.java index 8f332b2f3bd..dda26a73ef7 100644 --- a/src/main/java/org/prebid/server/bidder/criteo/CriteoExtBidResponse.java +++ b/src/main/java/org/prebid/server/bidder/criteo/CriteoExtBidResponse.java @@ -1,11 +1,12 @@ package org.prebid.server.bidder.criteo; import lombok.Value; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import java.util.List; @Value(staticConstructor = "of") public class CriteoExtBidResponse { - List igi; + List igi; } diff --git a/src/main/java/org/prebid/server/bidder/criteo/CriteoIgiExtBidResponse.java b/src/main/java/org/prebid/server/bidder/criteo/CriteoIgiExtBidResponse.java deleted file mode 100644 index 6bdac80ad2f..00000000000 --- a/src/main/java/org/prebid/server/bidder/criteo/CriteoIgiExtBidResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.prebid.server.bidder.criteo; - -import lombok.Value; - -import java.util.List; - -@Value(staticConstructor = "of") -public class CriteoIgiExtBidResponse { - - String impId; - - List igs; -} diff --git a/src/main/java/org/prebid/server/bidder/criteo/CriteoIgsIgiExtBidResponse.java b/src/main/java/org/prebid/server/bidder/criteo/CriteoIgsIgiExtBidResponse.java deleted file mode 100644 index b34d76e0646..00000000000 --- a/src/main/java/org/prebid/server/bidder/criteo/CriteoIgsIgiExtBidResponse.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.prebid.server.bidder.criteo; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.Value; - -@Value(staticConstructor = "of") -public class CriteoIgsIgiExtBidResponse { - - ObjectNode config; -} diff --git a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java index 5fb26e698fd..be29b4d1d4a 100644 --- a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java +++ b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java @@ -42,7 +42,8 @@ import org.prebid.server.proto.openrtb.ext.request.ix.ExtImpIx; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; import org.prebid.server.util.ObjectUtil; @@ -233,11 +234,12 @@ public Result> makeBids(BidderCall httpCall, BidRequ public CompositeBidderResponse makeBidderResponse(BidderCall httpCall, BidRequest bidRequest) { try { final IxBidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), IxBidResponse.class); - final List bidderErrors = new ArrayList<>(); + final List errors = new ArrayList<>(); + return CompositeBidderResponse.builder() - .bids(extractBids(bidRequest, bidResponse, bidderErrors)) - .fledgeAuctionConfigs(extractFledge(bidResponse)) - .errors(bidderErrors) + .bids(extractBids(bidRequest, bidResponse, errors)) + .igi(extractIgi(bidResponse)) + .errors(errors) .build(); } catch (DecodeException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); @@ -406,17 +408,16 @@ private static ExtBidPrebidVideo videoInfo(ExtBidPrebidVideo extBidPrebidVideo) : null; } - private List extractFledge(IxBidResponse bidResponse) { - return Optional.ofNullable(bidResponse) + private List extractIgi(IxBidResponse bidResponse) { + final List igs = Optional.ofNullable(bidResponse) .map(IxBidResponse::getExt) .map(IxExtBidResponse::getProtectedAudienceAuctionConfigs) .orElse(Collections.emptyList()) .stream() .filter(Objects::nonNull) - .map(ixAuctionConfig -> FledgeAuctionConfig.builder() - .impId(ixAuctionConfig.getBidId()) - .config(ixAuctionConfig.getConfig()) - .build()) + .map(config -> ExtIgiIgs.builder().impId(config.getBidId()).config(config.getConfig()).build()) .toList(); + + return igs.isEmpty() ? null : Collections.singletonList(ExtIgi.builder().igs(igs).build()); } } diff --git a/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java b/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java index d184aefc297..a82d6a72e63 100644 --- a/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java +++ b/src/main/java/org/prebid/server/bidder/medianet/MedianetBidder.java @@ -19,7 +19,8 @@ import org.prebid.server.json.DecodeException; import org.prebid.server.json.JacksonMapper; import org.prebid.server.proto.openrtb.ext.response.BidType; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; @@ -64,12 +65,9 @@ public final CompositeBidderResponse makeBidderResponse(BidderCall h } final List errors = new ArrayList<>(); - final List bids = extractBids(httpCall.getRequest().getPayload(), bidResponse, errors); - final List fledgeAuctionConfigs = extractFledge(bidResponse); - return CompositeBidderResponse.builder() - .bids(bids) - .fledgeAuctionConfigs(fledgeAuctionConfigs) + .bids(extractBids(httpCall.getRequest().getPayload(), bidResponse, errors)) + .igi(extractIgi(bidResponse)) .errors(errors) .build(); } @@ -138,15 +136,17 @@ private static BidType resolveBidTypeFromImpId(String impId, List imps) { return BidType.banner; } - private static List extractFledge(MedianetBidResponse bidResponse) { - return Optional.ofNullable(bidResponse) + private static List extractIgi(MedianetBidResponse bidResponse) { + final List igs = Optional.ofNullable(bidResponse) .map(MedianetBidResponse::getExt) .map(MedianetBidResponseExt::getIgi) .orElse(Collections.emptyList()) .stream() .map(InterestGroupAuctionIntent::getIgs) .flatMap(Collection::stream) - .map(e -> FledgeAuctionConfig.builder().impId(e.getImpId()).config(e.getConfig()).build()) + .map(igiIgs -> ExtIgiIgs.builder().impId(igiIgs.getImpId()).config(igiIgs.getConfig()).build()) .toList(); + + return igs.isEmpty() ? null : Collections.singletonList(ExtIgi.builder().igs(igs).build()); } } diff --git a/src/main/java/org/prebid/server/bidder/model/BidderSeatBid.java b/src/main/java/org/prebid/server/bidder/model/BidderSeatBid.java index 6cdc55cccb9..8022b8667f1 100644 --- a/src/main/java/org/prebid/server/bidder/model/BidderSeatBid.java +++ b/src/main/java/org/prebid/server/bidder/model/BidderSeatBid.java @@ -4,6 +4,7 @@ import lombok.Value; import org.prebid.server.bidder.Bidder; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; import java.util.Collections; @@ -53,9 +54,13 @@ public class BidderSeatBid { @Builder.Default List warnings = Collections.emptyList(); + @Deprecated @Builder.Default List fledgeAuctionConfigs = Collections.emptyList(); + @Builder.Default + List igi = Collections.emptyList(); + public BidderSeatBid with(List bids) { return toBuilder().bids(bids).build(); } diff --git a/src/main/java/org/prebid/server/bidder/model/BidderSeatBidInfo.java b/src/main/java/org/prebid/server/bidder/model/BidderSeatBidInfo.java index 37ff7cccfc2..38c9f86433c 100644 --- a/src/main/java/org/prebid/server/bidder/model/BidderSeatBidInfo.java +++ b/src/main/java/org/prebid/server/bidder/model/BidderSeatBidInfo.java @@ -1,15 +1,14 @@ package org.prebid.server.bidder.model; -import lombok.AllArgsConstructor; import lombok.Value; import org.prebid.server.auction.model.BidInfo; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; import java.util.List; -@AllArgsConstructor(staticName = "of") -@Value +@Value(staticConstructor = "of") public class BidderSeatBidInfo { List bidsInfos; @@ -20,9 +19,18 @@ public class BidderSeatBidInfo { List warnings; + @Deprecated(forRemoval = true) List fledgeAuctionConfigs; + List igi; + public BidderSeatBidInfo with(List bids) { - return BidderSeatBidInfo.of(bids, this.httpCalls, this.errors, this.warnings, this.fledgeAuctionConfigs); + return BidderSeatBidInfo.of( + bids, + this.httpCalls, + this.errors, + this.warnings, + this.fledgeAuctionConfigs, + this.igi); } } diff --git a/src/main/java/org/prebid/server/bidder/model/CompositeBidderResponse.java b/src/main/java/org/prebid/server/bidder/model/CompositeBidderResponse.java index 7e5a31f16b0..75a71e0864a 100644 --- a/src/main/java/org/prebid/server/bidder/model/CompositeBidderResponse.java +++ b/src/main/java/org/prebid/server/bidder/model/CompositeBidderResponse.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Value; import org.prebid.server.bidder.Bidder; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; import java.util.Collections; @@ -26,19 +27,12 @@ public class CompositeBidderResponse { */ List fledgeAuctionConfigs; + List igi; + public static CompositeBidderResponse empty() { return builder().build(); } - public static CompositeBidderResponse withBids(List bids, - List fledgeAuctionConfigs) { - - return builder() - .bids(bids) - .fledgeAuctionConfigs(fledgeAuctionConfigs) - .build(); - } - public static CompositeBidderResponse withError(BidderError error) { return builder().errors(Collections.singletonList(error)).build(); } diff --git a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java index 6cdae97b5cb..f37547e90cf 100644 --- a/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java +++ b/src/main/java/org/prebid/server/bidder/openx/OpenxBidder.java @@ -30,7 +30,8 @@ import org.prebid.server.proto.openrtb.ext.request.openx.ExtImpOpenx; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; @@ -88,9 +89,13 @@ public Result>> makeHttpRequests(BidRequest bidRequ @Override public CompositeBidderResponse makeBidderResponse(BidderCall httpCall, BidRequest bidRequest) { try { - final OpenxBidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), - OpenxBidResponse.class); - return CompositeBidderResponse.withBids(extractBids(bidRequest, bidResponse), extractFledge(bidResponse)); + final OpenxBidResponse bidResponse = mapper.decodeValue( + httpCall.getResponse().getBody(), OpenxBidResponse.class); + + return CompositeBidderResponse.builder() + .bids(extractBids(bidRequest, bidResponse)) + .igi(extractIgi(bidResponse)) + .build(); } catch (DecodeException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); } @@ -312,14 +317,16 @@ private static BidType getBidType(Bid bid, Map impIdToBidType) return impIdToBidType.getOrDefault(bid.getImpid(), BidType.banner); } - private static List extractFledge(OpenxBidResponse bidResponse) { - return Optional.ofNullable(bidResponse) + private static List extractIgi(OpenxBidResponse bidResponse) { + final List igs = Optional.ofNullable(bidResponse) .map(OpenxBidResponse::getExt) .map(OpenxBidResponseExt::getFledgeAuctionConfigs) .orElse(Collections.emptyMap()) .entrySet() .stream() - .map(e -> FledgeAuctionConfig.builder().impId(e.getKey()).config(e.getValue()).build()) + .map(ext -> ExtIgiIgs.builder().impId(ext.getKey()).config(ext.getValue()).build()) .toList(); + + return igs.isEmpty() ? null : Collections.singletonList(ExtIgi.builder().igs(igs).build()); } } diff --git a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java index d84465416f8..4a9e9818688 100644 --- a/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java +++ b/src/main/java/org/prebid/server/bidder/pubmatic/PubmaticBidder.java @@ -43,7 +43,8 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.util.BidderUtil; import org.prebid.server.util.HttpUtil; import org.prebid.server.util.StreamUtil; @@ -496,11 +497,14 @@ public Result> makeBids(BidderCall httpCall, BidRequ @Override public CompositeBidderResponse makeBidderResponse(BidderCall httpCall, BidRequest bidRequest) { try { - final List bidderErrors = new ArrayList<>(); final PubmaticBidResponse bidResponse = mapper.decodeValue( - httpCall.getResponse().getBody(), - PubmaticBidResponse.class); - return CompositeBidderResponse.withBids(extractBids(bidResponse, bidderErrors), extractFledge(bidResponse)); + httpCall.getResponse().getBody(), PubmaticBidResponse.class); + final List errors = new ArrayList<>(); + + return CompositeBidderResponse.builder() + .bids(extractBids(bidResponse, errors)) + .igi(extractIgi(bidResponse)) + .build(); } catch (DecodeException | PreBidException e) { return CompositeBidderResponse.withError(BidderError.badServerResponse(e.getMessage())); } @@ -609,14 +613,16 @@ private static Integer getDealPriority(PubmaticBidExt bidExt) { .orElse(null); } - private static List extractFledge(PubmaticBidResponse bidResponse) { - return Optional.ofNullable(bidResponse) + private static List extractIgi(PubmaticBidResponse bidResponse) { + final List igs = Optional.ofNullable(bidResponse) .map(PubmaticBidResponse::getExt) .map(PubmaticExtBidResponse::getFledgeAuctionConfigs) .orElse(Collections.emptyMap()) .entrySet() .stream() - .map(e -> FledgeAuctionConfig.builder().impId(e.getKey()).config(e.getValue()).build()) + .map(config -> ExtIgiIgs.builder().impId(config.getKey()).config(config.getValue()).build()) .toList(); + + return igs.isEmpty() ? null : Collections.singletonList(ExtIgi.builder().igs(igs).build()); } } diff --git a/src/main/java/org/prebid/server/handler/SetuidHandler.java b/src/main/java/org/prebid/server/handler/SetuidHandler.java index 728285fb0f6..450cc1f7efa 100644 --- a/src/main/java/org/prebid/server/handler/SetuidHandler.java +++ b/src/main/java/org/prebid/server/handler/SetuidHandler.java @@ -138,6 +138,7 @@ private static void validateUsersyncersDuplicates(BidderCatalog bidderCatalog) { .map(Usersyncer::getCookieFamilyName) .filter(Predicate.not(StreamUtil.distinctBy(Function.identity()))) .distinct() + .sorted() .toList(); if (!duplicatedCookieFamilyNames.isEmpty()) { diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java index cb325bd088a..33b326b2638 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtRequestPrebid.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Builder; import lombok.Value; +import org.prebid.server.auction.model.PaaFormat; import org.prebid.server.floors.model.PriceFloorRules; import org.prebid.server.json.deserializer.IntegerFlagDeserializer; @@ -184,4 +185,9 @@ public class ExtRequestPrebid { */ ExtRequestPrebidSdk sdk; + /** + * Defines the contract for bidrequest.ext.prebid.paaformat + */ + @JsonProperty("paaformat") + PaaFormat paaFormat; } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtBidResponse.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtBidResponse.java index 115ce7b3ff9..6f4b3904fd7 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtBidResponse.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtBidResponse.java @@ -48,6 +48,11 @@ public class ExtBidResponse { */ Map usersync; + /** + * Defines the contract for bidresponse.ext.igi + */ + List igi; + /** * Defines the contract for bidresponse.ext.prebid */ diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgi.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgi.java new file mode 100644 index 00000000000..49e84d9739f --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgi.java @@ -0,0 +1,20 @@ +package org.prebid.server.proto.openrtb.ext.response; + +import lombok.Builder; +import lombok.Value; + +import java.util.List; + +/** + * Defines the contract for bidresponse.ext.igi + */ +@Builder(toBuilder = true) +@Value(staticConstructor = "of") +public class ExtIgi { + + String impid; + + List igb; + + List igs; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgb.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgb.java new file mode 100644 index 00000000000..aca3758c7aa --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgb.java @@ -0,0 +1,22 @@ +package org.prebid.server.proto.openrtb.ext.response; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class ExtIgiIgb { + + String origin; + + Double maxbid; + + @Builder.Default + String cur = "USD"; + + JsonNode pbs; + + ObjectNode ps; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgs.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgs.java new file mode 100644 index 00000000000..a243380bb4e --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgs.java @@ -0,0 +1,18 @@ +package org.prebid.server.proto.openrtb.ext.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Builder; +import lombok.Value; + +@Builder(toBuilder = true) +@Value(staticConstructor = "of") +public class ExtIgiIgs { + + @JsonProperty("impid") + String impId; + + ObjectNode config; + + ExtIgiIgsExt ext; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgsExt.java b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgsExt.java new file mode 100644 index 00000000000..979b7fe6e13 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/response/ExtIgiIgsExt.java @@ -0,0 +1,11 @@ +package org.prebid.server.proto.openrtb.ext.response; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtIgiIgsExt { + + String bidder; + + String adapter; +} diff --git a/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java b/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java index 9943535aa7e..ac7da04dd31 100644 --- a/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java +++ b/src/main/java/org/prebid/server/settings/model/AccountAuctionConfig.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Builder; import lombok.Value; +import org.prebid.server.auction.model.PaaFormat; import org.prebid.server.spring.config.bidder.model.MediaType; import java.util.Map; @@ -49,4 +50,7 @@ public class AccountAuctionConfig { @JsonProperty("privacysandbox") AccountPrivacySandboxConfig privacySandbox; + + @JsonProperty("paaformat") + PaaFormat paaFormat; } diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index deaa768320c..bc7352f5a51 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -807,6 +807,7 @@ CacheDefaultTtlProperties cacheDefaultTtlProperties( @Bean BidResponseCreator bidResponseCreator( + @Value("${logging.sampling-rate:0.01}") double logSamplingRate, CoreCacheService coreCacheService, BidderCatalog bidderCatalog, VastModifier vastModifier, @@ -819,11 +820,13 @@ BidResponseCreator bidResponseCreator( @Value("${settings.targeting.truncate-attr-chars}") int truncateAttrChars, Clock clock, JacksonMapper mapper, + Metrics metrics, @Value("${cache.banner-ttl-seconds:#{null}}") Integer bannerCacheTtl, @Value("${cache.video-ttl-seconds:#{null}}") Integer videoCacheTtl, CacheDefaultTtlProperties cacheDefaultTtlProperties) { return new BidResponseCreator( + logSamplingRate, coreCacheService, bidderCatalog, vastModifier, @@ -836,6 +839,7 @@ BidResponseCreator bidResponseCreator( truncateAttrChars, clock, mapper, + metrics, CacheTtl.of(bannerCacheTtl, videoCacheTtl), cacheDefaultTtlProperties); } diff --git a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy index 352080844ab..54e0a451fc6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/bidder/BidderName.groovy @@ -18,6 +18,7 @@ enum BidderName { APPNEXUS("appnexus"), RUBICON_ALIAS("rubiconAlias"), OPENX("openx"), + OPENX_ALIAS("openxalias"), ACEEX("aceex"), ACUITYADS("acuityads"), AAX("aax"), diff --git a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy index 4e423c03312..8dc9831e3fa 100644 --- a/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/config/AccountAuctionConfig.groovy @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming import groovy.transform.ToString import org.prebid.server.functional.model.bidder.BidderName import org.prebid.server.functional.model.request.auction.BidAdjustment +import org.prebid.server.functional.model.request.auction.PaaFormat import org.prebid.server.functional.model.request.auction.Targeting import org.prebid.server.functional.model.response.auction.MediaType @@ -23,6 +24,7 @@ class AccountAuctionConfig { AccountEventsConfig events AccountPriceFloorsConfig priceFloors Targeting targeting + PaaFormat paaformat @JsonProperty("preferredmediatype") Map preferredMediaType @JsonProperty("privacysandbox") diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy new file mode 100644 index 00000000000..f880b7bc634 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/AuctionEnvironment.groovy @@ -0,0 +1,19 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue +import org.prebid.server.functional.util.PBSUtils + +enum AuctionEnvironment { + + NOT_SUPPORTED(0), + DEVICE_ORCHESTRATED(1), + SERVER_ORCHESTRATED(3), + UNKNOWN(Integer.MAX_VALUE), + + @JsonValue + private int value + + AuctionEnvironment(Integer value) { + this.value = value + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy index 605f286c803..05a43bbb528 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Bidder.groovy @@ -22,6 +22,8 @@ class Bidder { AppNexus appNexus Openx openx Ix ix + @JsonProperty("openxalias") + Openx openxAlias static Bidder getDefaultBidder() { new Bidder().tap { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy index e817a4540a0..55c626d7bd6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/ImpExt.groovy @@ -23,10 +23,13 @@ class ImpExt { String tid String gpid String sid - Integer ae + @JsonProperty("ae") + AuctionEnvironment auctionEnvironment String all String skadn String general + @JsonProperty("igs") + List interestGroupAuctionSupports AnyUnsupportedBidder anyUnsupportedBidder static ImpExt getDefaultImpExt() { diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy new file mode 100644 index 00000000000..31d3628587d --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/InterestGroupAuctionSupport.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionSupport { + + @JsonProperty("ae") + AuctionEnvironment auctionEnvironment +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy new file mode 100644 index 00000000000..79f41e953fe --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/PaaFormat.groovy @@ -0,0 +1,13 @@ +package org.prebid.server.functional.model.request.auction + +import com.fasterxml.jackson.annotation.JsonValue + +enum PaaFormat { + + ORIGINAL, IAB, INVALID + + @JsonValue + String getValue() { + name().toLowerCase() + } +} diff --git a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy index 6a6ae093ea5..30149d1a5ee 100644 --- a/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/request/auction/Prebid.groovy @@ -40,6 +40,7 @@ class Prebid { PrebidModulesConfig modules PrebidAnalytics analytics StoredAuctionResponse storedAuctionResponse + PaaFormat paaFormat static class Channel { diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy index 3ccb3384e89..d1d282d663a 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/BidResponseExt.groovy @@ -1,5 +1,6 @@ package org.prebid.server.functional.model.response.auction +import com.fasterxml.jackson.annotation.JsonProperty import groovy.transform.ToString import org.prebid.server.functional.model.response.BidderError import org.prebid.server.functional.model.response.Debug @@ -15,4 +16,6 @@ class BidResponseExt { Map usersync BidResponsePrebid prebid Map> warnings + @JsonProperty("igi") + List interestGroupAuctionIntent } diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy new file mode 100644 index 00000000000..b8d1bf80d38 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyer.groovy @@ -0,0 +1,17 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString +import org.prebid.server.functional.model.Currency + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionBuyer { + + String origin + BigDecimal maxBid + Currency cur + Map pbs + InterestGroupAuctionBuyerExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy new file mode 100644 index 00000000000..4404491246c --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionBuyerExt.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionBuyerExt { + + String bidder + String adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy new file mode 100644 index 00000000000..29ff15d4eeb --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntent.groovy @@ -0,0 +1,18 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionIntent { + + String impId + @JsonProperty("igb") + List interestGroupAuctionBuyer + @JsonProperty("igs") + List interestGroupAuctionSeller + InterestGroupAuctionIntentExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy new file mode 100644 index 00000000000..29aeaf980da --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionIntentExt.groovy @@ -0,0 +1,11 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString +import org.prebid.server.functional.model.bidder.BidderName + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionIntentExt { + + BidderName bidder + BidderName adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy new file mode 100644 index 00000000000..192f5c2f611 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSeller.groovy @@ -0,0 +1,14 @@ +package org.prebid.server.functional.model.response.auction + +import com.fasterxml.jackson.databind.PropertyNamingStrategies +import com.fasterxml.jackson.databind.annotation.JsonNaming +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +@JsonNaming(PropertyNamingStrategies.LowerCaseStrategy) +class InterestGroupAuctionSeller { + + String impId + Map config + InterestGroupAuctionSellerExt ext +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy new file mode 100644 index 00000000000..fcecf1b5480 --- /dev/null +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/InterestGroupAuctionSellerExt.groovy @@ -0,0 +1,10 @@ +package org.prebid.server.functional.model.response.auction + +import groovy.transform.ToString + +@ToString(includeNames = true, ignoreNulls = true) +class InterestGroupAuctionSellerExt { + + String bidder + String adapter +} diff --git a/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy b/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy index 2ba60b226c1..b5d5289d5a6 100644 --- a/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy +++ b/src/test/groovy/org/prebid/server/functional/model/response/auction/OpenxBidResponse.groovy @@ -1,6 +1,5 @@ package org.prebid.server.functional.model.response.auction - import groovy.transform.ToString import org.prebid.server.functional.model.request.auction.BidRequest diff --git a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy index a9da07cbd6d..de5d742c6f4 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/BidderParamsSpec.groovy @@ -6,6 +6,7 @@ import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.db.StoredImp import org.prebid.server.functional.model.db.StoredRequest import org.prebid.server.functional.model.request.amp.AmpRequest +import org.prebid.server.functional.model.request.auction.AuctionEnvironment import org.prebid.server.functional.model.request.auction.Banner import org.prebid.server.functional.model.request.auction.BidRequest import org.prebid.server.functional.model.request.auction.Device @@ -15,6 +16,7 @@ import org.prebid.server.functional.model.request.auction.Imp import org.prebid.server.functional.model.request.auction.ImpExt import org.prebid.server.functional.model.request.auction.ImpExtContext import org.prebid.server.functional.model.request.auction.ImpExtContextData +import org.prebid.server.functional.model.request.auction.InterestGroupAuctionSupport import org.prebid.server.functional.model.request.auction.Native import org.prebid.server.functional.model.request.auction.PrebidOptions import org.prebid.server.functional.model.request.auction.PrebidStoredRequest @@ -35,6 +37,10 @@ import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS import static org.prebid.server.functional.model.bidder.CompressionType.GZIP import static org.prebid.server.functional.model.bidder.CompressionType.NONE import static org.prebid.server.functional.model.request.auction.Asset.titleAsset +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.DEVICE_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.NOT_SUPPORTED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.SERVER_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.UNKNOWN import static org.prebid.server.functional.model.request.auction.DistributionChannel.APP import static org.prebid.server.functional.model.request.auction.DistributionChannel.DOOH import static org.prebid.server.functional.model.request.auction.DistributionChannel.SITE @@ -866,7 +872,7 @@ class BidderParamsSpec extends BaseSpec { prebid.bidder.generic = null prebid.adUnitCode = PBSUtils.randomString generic = new Generic() - ae = PBSUtils.randomNumber + auctionEnvironment = PBSUtils.getRandomEnum(AuctionEnvironment) all = PBSUtils.randomNumber context = new ImpExtContext(data: new ImpExtContextData()) data = new ImpExtContextData(pbAdSlot: PBSUtils.randomString) @@ -892,7 +898,7 @@ class BidderParamsSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) verifyAll(bidderRequest.imp[0].ext) { bidder == impExt.generic - ae == impExt.ae + auctionEnvironment == impExt.auctionEnvironment all == impExt.all context == impExt.context data == impExt.data @@ -1230,4 +1236,66 @@ class BidderParamsSpec extends BaseSpec { cleanup: "Stop and remove pbs container" pbsServiceFactory.removeContainer(pbsConfig) } + + def "PBS should add auction environment to imp.ext.igs when it is present in imp.ext and imp.ext.igs is empty"() { + given: "Default bid request with populated imp.ext" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = requestedAuctionEnvironment + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: null)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].{ae/ext.igs.ae} same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == requestedAuctionEnvironment + assert bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment == requestedAuctionEnvironment + + where: + requestedAuctionEnvironment << [NOT_SUPPORTED, DEVICE_ORCHESTRATED] + } + + def "PBS shouldn't add unsupported auction environment to imp.ext.igs when it is present in imp.ext and imp.ext.igs is empty"() { + given: "Default bid request with populated imp.ext" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = requestedAuctionEnvironment + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: null)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].ae same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == requestedAuctionEnvironment + assert !bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment + + where: + requestedAuctionEnvironment << [SERVER_ORCHESTRATED, UNKNOWN] + } + + def "PBS shouldn't change auction environment in imp.ext.igs when it is present in both imp.ext and imp.ext.igs"() { + given: "Default bid request with populated imp.ext" + def extAuctionEnv = PBSUtils.getRandomEnum(AuctionEnvironment) + def extIgsAuctionEnv = PBSUtils.getRandomEnum(AuctionEnvironment) + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.tap { + auctionEnvironment = extAuctionEnv + interestGroupAuctionSupports = [new InterestGroupAuctionSupport(auctionEnvironment: extIgsAuctionEnv)] + } + } + + when: "PBS processes auction request" + defaultPbsService.sendAuctionRequest(bidRequest) + + then: "Bidder request should imp[].{ae/ext.igs.ae} same value as requested" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp[0].ext.auctionEnvironment == extAuctionEnv + assert bidderRequest.imp[0].ext.interestGroupAuctionSupports[0].auctionEnvironment == extIgsAuctionEnv + } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy index 45540134252..1adf84c32fd 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/bidder/openx/OpenxSpec.groovy @@ -1,35 +1,95 @@ package org.prebid.server.functional.tests.bidder.openx +import org.prebid.server.functional.model.Currency import org.prebid.server.functional.model.bidder.Openx +import org.prebid.server.functional.model.config.AccountAuctionConfig +import org.prebid.server.functional.model.config.AccountConfig +import org.prebid.server.functional.model.db.Account import org.prebid.server.functional.model.request.auction.BidRequest +import org.prebid.server.functional.model.request.auction.PaaFormat +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionBuyer +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionBuyerExt +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionIntent +import org.prebid.server.functional.model.response.auction.InterestGroupAuctionSeller import org.prebid.server.functional.model.response.auction.OpenxBidResponse import org.prebid.server.functional.model.response.auction.OpenxBidResponseExt +import org.prebid.server.functional.service.PrebidServerException import org.prebid.server.functional.service.PrebidServerService import org.prebid.server.functional.tests.BaseSpec import org.prebid.server.functional.util.PBSUtils import spock.lang.Shared +import java.time.Instant + +import static org.prebid.server.functional.model.bidder.BidderName.OPENX +import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS +import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.DEVICE_ORCHESTRATED +import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.NOT_SUPPORTED +import static org.prebid.server.functional.model.request.auction.PaaFormat.IAB +import static org.prebid.server.functional.model.request.auction.PaaFormat.ORIGINAL +import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID import static org.prebid.server.functional.testcontainers.Dependencies.networkServiceContainer class OpenxSpec extends BaseSpec { private static final Map OPENX_CONFIG = ["adapters.openx.enabled" : "true", "adapters.openx.endpoint": "$networkServiceContainer.rootUri/auction".toString()] + private static final Map OPENX_ALIAS_CONFIG = ["adapters.openx.aliases.openxalias.enabled" : "true", + "adapters.openx.aliases.openxalias.endpoint": "$networkServiceContainer.rootUri/auction".toString()] @Shared PrebidServerService pbsService = pbsServiceFactory.getService(OPENX_CONFIG) - def "PBS should populate fledge config when bid response with fledge and imp[0].ext.ae = 1"() { + def "PBS should populate fledge config by default when bid response with fledge"() { given: "Default basic BidRequest with ae and openx bidder" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 1 + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx } and: "Default bid response with fledge config" def impId = bidRequest.imp[0].id def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] - def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == impId + assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionBuyer + + and: "PBS response shouldn't contain igs config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionSeller + } + + def "PBS should populate fledge config when bid response with fledge and ext.prebid.paaFormat = ORIGINAL"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = ORIGINAL + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { ext = new OpenxBidResponseExt().tap { fledgeAuctionConfigs = [(impId): fledgeConfig] } @@ -48,13 +108,61 @@ class OpenxSpec extends BaseSpec { assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionBuyer + + and: "PBS response shouldn't contain igs config" + assert !response.ext?.interestGroupAuctionIntent?.interestGroupAuctionSeller } - def "PBS shouldn't populate fledge config when imp[0].ext.ae = 0"() { + def "PBS should take precedence request paa format over account value when both specified"() { + given: "Default bid request with openx" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Save account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(paaformat: ORIGINAL)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + } + + def "PBS shouldn't populate fledge config when bid response with fledge and ext.prebid.paaFormat = IAB"() { given: "Default basic BidRequest without ae" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 0 + imp[0].ext.auctionEnvironment = NOT_SUPPORTED imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB } and: "Default bid response" @@ -73,12 +181,21 @@ class OpenxSpec extends BaseSpec { then: "PBS response shouldn't contain fledge config" assert !response.ext.prebid.fledge + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == OPENX.value } - def "PBS shouldn't populate fledge config when imp[0].ext.ae = 1 and bid response didn't return fledge config"() { + def "PBS shouldn't populate fledge config when bid response didn't return fledge config"() { given: "Default basic BidRequest without ae" def bidRequest = BidRequest.defaultBidRequest.tap { - imp[0].ext.ae = 1 imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx } @@ -97,5 +214,324 @@ class OpenxSpec extends BaseSpec { then: "PBS response shouldn't contain fledge config" assert !response.ext.prebid.fledge + + and: "PBS response shouldn't contain igi config" + assert !response?.ext?.interestGroupAuctionIntent + } + + def "PBS should populate fledge and iab output config when bid response with fledge and paa formant IAB"() { + given: "Default bid request with openx" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = requestPaaFormant + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Save account in the DB" + def accountConfig = new AccountConfig(auction: new AccountAuctionConfig(paaformat: accountPaaFormat)) + def account = new Account(uuid: bidRequest.site.publisher.id, config: accountConfig) + accountDao.save(account) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value + assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value + + and: "PBS response shouldn't contain igb config" + assert !response.ext?.interestGroupAuctionIntent?[0]?.interestGroupAuctionBuyer + + where: + accountPaaFormat | requestPaaFormant + IAB | IAB + null | IAB + IAB | null + } + + def "PBS should populate fledge config by default when bid response with fledge and requested aliases"() { + given: "PBS config with alias config" + def pbsService = pbsServiceFactory.getService(OPENX_CONFIG + OPENX_ALIAS_CONFIG) + + and: "Default basic BidRequest with ae and bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.openxAlias = Openx.defaultOpenx + ext.prebid.aliases = [(OPENX_ALIAS.value): OPENX] + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX_ALIAS).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == impId + assert auctionConfigs[0].bidder == OPENX_ALIAS.value + assert auctionConfigs[0].adapter == OPENX.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + } + + def "PBS should populate iab config when bid response with fledge and requested aliases"() { + given: "PBS config" + def pbsService = pbsServiceFactory.getService(OPENX_CONFIG + OPENX_ALIAS_CONFIG) + + and: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.generic = null + imp[0].ext.prebid.bidder.openxAlias = Openx.defaultOpenx + ext.prebid.aliases = [(OPENX_ALIAS.value): OPENX] + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX_ALIAS).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response should contain igs config" + def interestGroupAuctionSeller = response.ext.interestGroupAuctionIntent[0].interestGroupAuctionSeller[0] + assert interestGroupAuctionSeller.impId == impId + assert interestGroupAuctionSeller.config == fledgeConfig + assert interestGroupAuctionSeller.ext.bidder == OPENX_ALIAS.value + assert interestGroupAuctionSeller.ext.adapter == OPENX.value + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent?[0].interestGroupAuctionBuyer + } + + def "PBS should populate fledge config by default when bid response with fledge and imp mismatched"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + } + + and: "Default bid response with fledge config" + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(fledgeImpId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + def auctionConfigs = response.ext?.prebid?.fledge?.auctionConfigs + assert auctionConfigs?.size() == 1 + assert auctionConfigs[0].impId == fledgeImpId + assert auctionConfigs[0].bidder == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].adapter == bidResponse.seatbid[0].seat.value + assert auctionConfigs[0].config == fledgeConfig + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + where: + fledgeImpId << [PBSUtils.randomString, PBSUtils.randomNumber as String, WILDCARD.value] + } + + def "PBS should log error and not populated fledge impId when bidder respond with not empty config, but an empty impid"() { + given: "Start time" + def startTime = Instant.now() + + and: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = IAB + } + + and: "Default bid response with fledge config without imp" + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(""): fledgeConfig] as Map + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + and: "Flush metrics" + flushMetrics(pbsService) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response shouldn't contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + and: "PBS log should contain error" + def logs = pbsService.getLogsByTime(startTime) + assert getLogsByText(logs, "ExtIgiIgs with absent impId from bidder: ${OPENX.value}") + + and: "Bid response should contain warning" + assert response.ext.warnings[PREBID]?.code == [999] + assert response.ext.warnings[PREBID]?.message == + ["ExtIgiIgs with absent impId from bidder: ${OPENX.value}" as String] + + and: "Alert.general metric should be updated" + def metrics = pbsService.sendCollectedMetricsRequest() + assert metrics["alerts.general" as String] == 1 + } + + def "PBS shouldn't populate fledge or igi config when bidder respond with igb"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + } + + and: "Default bid response with igb config" + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + interestGroupAuctionIntent = [new InterestGroupAuctionIntent( + interestGroupAuctionBuyer: [new InterestGroupAuctionBuyer( + origin: PBSUtils.randomString, + maxBid: PBSUtils.randomDecimal, + cur: PBSUtils.getRandomEnum(Currency), + pbs: [(PBSUtils.randomString): PBSUtils.randomString], + ext: new InterestGroupAuctionBuyerExt( + bidder: PBSUtils.randomString, + adapter: PBSUtils.randomString + ) + )]) + ] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + } + + def "PBS should throw error when requested unknown paa format"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = PaaFormat.INVALID + } + + and: "Default bid response with fledge config" + def impId = bidRequest.imp[0].id + def fledgeConfig = [(PBSUtils.randomString): PBSUtils.randomString] + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + fledgeAuctionConfigs = [(impId): fledgeConfig] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + pbsService.sendAuctionRequest(bidRequest) + + then: "Request should fail with error" + def exception = thrown(PrebidServerException) + assert exception.responseBody.startsWith("Invalid request format: Error decoding bidRequest: " + + "Cannot deserialize value of type `org.prebid.server.auction.model.PaaFormat` " + + "from String \"invalid\": not one of the values accepted for Enum class: [original, iab]") + } + + def "PBS shouldn't cause error when igs and igb empty array"() { + given: "Default basic BidRequest with ae and openx bidder" + def bidRequest = BidRequest.defaultBidRequest.tap { + imp[0].ext.auctionEnvironment = DEVICE_ORCHESTRATED + imp[0].ext.prebid.bidder.openx = Openx.defaultOpenx + ext.prebid.paaFormat = paaFormat + } + + and: "Default bid response with igs config" + def bidResponse = OpenxBidResponse.getDefaultBidResponse(bidRequest, OPENX).tap { + ext = new OpenxBidResponseExt().tap { + interestGroupAuctionIntent = [new InterestGroupAuctionIntent( + interestGroupAuctionSeller: interestGroupAuctionSeller, + interestGroupAuctionBuyer: interestGroupAuctionBuyer + )] + } + } + + and: "Set bidder response" + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "PBS response should contain fledge config" + assert !response.ext?.prebid?.fledge?.auctionConfigs + + and: "PBS response shouldn't contain igi config" + assert !response.ext?.interestGroupAuctionIntent + + where: + paaFormat | interestGroupAuctionSeller | interestGroupAuctionBuyer + IAB | [new InterestGroupAuctionSeller()] | [new InterestGroupAuctionBuyer()] + ORIGINAL | [] | [] } } diff --git a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java index 19181bf3503..04b7d279956 100644 --- a/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java +++ b/src/test/java/org/prebid/server/auction/BidResponseCreatorTest.java @@ -44,6 +44,7 @@ import org.prebid.server.auction.model.CachedDebugLog; import org.prebid.server.auction.model.CategoryMappingResult; import org.prebid.server.auction.model.MultiBidConfig; +import org.prebid.server.auction.model.PaaFormat; import org.prebid.server.auction.model.TargetingInfo; import org.prebid.server.auction.model.TimeoutContext; import org.prebid.server.auction.model.debug.DebugContext; @@ -68,6 +69,8 @@ import org.prebid.server.hooks.execution.v1.bidder.BidderResponsePayloadImpl; import org.prebid.server.identity.IdGenerator; import org.prebid.server.identity.IdGeneratorType; +import org.prebid.server.metric.MetricName; +import org.prebid.server.metric.Metrics; import org.prebid.server.proto.openrtb.ext.ExtIncludeBrandCategory; import org.prebid.server.proto.openrtb.ext.request.ExtDeal; import org.prebid.server.proto.openrtb.ext.request.ExtDealLine; @@ -91,10 +94,15 @@ import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; import org.prebid.server.proto.openrtb.ext.response.ExtBidResponse; +import org.prebid.server.proto.openrtb.ext.response.ExtBidResponseFledge; import org.prebid.server.proto.openrtb.ext.response.ExtBidResponsePrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidderError; import org.prebid.server.proto.openrtb.ext.response.ExtDebugTrace; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgb; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgsExt; import org.prebid.server.proto.openrtb.ext.response.ExtResponseCache; import org.prebid.server.proto.openrtb.ext.response.ExtResponseDebug; import org.prebid.server.proto.openrtb.ext.response.ExtTraceActivityInfrastructure; @@ -169,6 +177,7 @@ public class BidResponseCreatorTest extends VertxTest { private static final String IMP_ID = "impId1"; private static final String BID_ADM = "adm"; private static final String BID_NURL = "nurl"; + private static final String PREBID = "prebid"; @Mock private CoreCacheService coreCacheService; @@ -192,6 +201,10 @@ public class BidResponseCreatorTest extends VertxTest { private CacheTtl mediaTypeCacheTtl; @Mock(strictness = LENIENT) private CacheDefaultTtlProperties cacheDefaultProperties; + @Mock(strictness = LENIENT) + private BidderAliases aliases; + @Mock(strictness = LENIENT) + private Metrics metrics; @Spy private WinningBidComparatorFactory winningBidComparatorFactory; @@ -216,6 +229,8 @@ public void setUp() { given(cacheDefaultProperties.getAudioTtl()).willReturn(null); given(cacheDefaultProperties.getNativeTtl()).willReturn(null); + given(aliases.resolveBidder(any())).willAnswer(request -> request.getArgument(0)); + given(categoryMappingService.createCategoryMapping(any(), any(), any())) .willAnswer(invocationOnMock -> Future.succeededFuture( CategoryMappingResult.of(emptyMap(), emptyMap(), invocationOnMock.getArgument(0), null))); @@ -279,7 +294,7 @@ public void shouldPassBidWithGeneratedIdAndPreserveExtFieldsWhenIdGeneratorTypeU givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder() @@ -314,7 +329,7 @@ public void shouldSkipBidderWhenRejectedByProcessedBidderResponseHooks() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -349,7 +364,7 @@ public void shouldPassRequestModifiedByBidderRequestHooks() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -378,7 +393,7 @@ public void shouldPassOriginalTimeoutToCacheServiceIfCachingIsRequested() { givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then verify(coreCacheService).cacheBidsOpenrtb(anyList(), any(), any(), any()); @@ -431,8 +446,7 @@ public void shouldRequestCacheServiceWithExpectedArguments() { givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, - MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -494,8 +508,7 @@ imp1, imp2, givenImp("impId3"), givenImp("impId4"))) givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, - MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor> bidsArgumentCaptor = ArgumentCaptor.forClass(List.class); @@ -561,7 +574,7 @@ public void shouldRequestCacheServiceWithVideoBidsToModify() { givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final EventsContext expectedEventContext = EventsContext.builder() @@ -622,7 +635,7 @@ public void shouldModifyBidAdmWhenBidVideoAndVastModifierReturnValue() { .willReturn(modifiedVast); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -650,7 +663,7 @@ public void shouldCallCacheServiceEvenRoundedCpmIsZero() { givenCacheServiceResult(singletonList(CacheInfo.empty())); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor> bidsArgumentCaptor = ArgumentCaptor.forClass(List.class); @@ -673,7 +686,7 @@ public void shouldSetExpectedConstantResponseFields() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, null, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, null, aliases, MULTI_BIDS).result(); // then final BidResponse responseWithExpectedFields = BidResponse.builder() @@ -700,7 +713,7 @@ public void shouldSetNbrValueTwoAndEmptySeatbidWhenIncomingBidResponsesAreEmpty( final AuctionContext auctionContext = givenAuctionContext(givenBidRequest(givenImp())); // when - final BidResponse bidResponse = target.create(auctionContext, null, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, null, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse).returns(0, BidResponse::getNbr); @@ -718,7 +731,7 @@ public void shouldSetNbrValueTwoAndEmptySeatbidWhenIncomingBidResponsesDoNotCont contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, null, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, null, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse).returns(0, BidResponse::getNbr); @@ -739,7 +752,7 @@ public void shouldSetNbrNullAndPopulateSeatbidWhenAtLeastOneBidIsPresent() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getNbr()).isNull(); @@ -761,7 +774,7 @@ public void shouldSkipBidderResponsesWhereSeatBidContainEmptyBids() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1); @@ -789,7 +802,7 @@ public void shouldOverrideBidIdWhenIdGeneratorIsUUID() { given(idGenerator.generateId()).willReturn("de7fc739-0a6e-41ad-8961-701c30c82166"); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -851,7 +864,7 @@ public void shouldUseGeneratedBidIdForEventAndCacheWhenIdGeneratorIsUUIDAndEvent .willReturn(events); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ExtBidPrebid extBidPrebid = ExtBidPrebid.builder() @@ -896,7 +909,7 @@ public void shouldSetExpectedResponseSeatBidAndBidFields() { givenCacheServiceResult(emptyList()); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ObjectNode expectedBidExt = mapper.createObjectNode(); @@ -941,7 +954,7 @@ public void shouldUpdateCacheDebugLogWithExtBidResponseWhenEnabledAndBidsReturne .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(auctionContext.getCachedDebugLog().buildCacheBody()) @@ -957,7 +970,7 @@ public void shouldUpdateCacheDebugLogWithExtBidResponseWhenEnabledAndNoBidsRetur contextBuilder -> contextBuilder.cachedDebugLog(new CachedDebugLog(true, 100, null, jacksonMapper))); // when - target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(auctionContext.getCachedDebugLog().buildCacheBody()) @@ -991,7 +1004,7 @@ public void shouldUseBidsReturnedInCategoryMapperResultAndUpdateErrors() { singletonList("Filtered bid 2")))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1017,7 +1030,7 @@ public void shouldThrowExceptionWhenCategoryMappingThrowsPrebidException() { .willReturn(Future.failedFuture(new InvalidRequestException("category exception"))); // when - final Future result = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future result = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // then assertThat(result.failed()).isTrue(); @@ -1053,7 +1066,7 @@ public void shouldNotWriteSkadnAttributeToBidderSection() { givenCacheServiceResult(emptyList()); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ObjectNode expectedBidExt = mapper.createObjectNode(); @@ -1112,7 +1125,7 @@ public void shouldAddTypeToNativeBidAdm() throws JsonProcessingException { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -1173,7 +1186,7 @@ public void shouldReturnEmptyAssetIfImageTypeIsEmpty() throws JsonProcessingExce contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -1228,7 +1241,7 @@ public void shouldReturnEmptyAssetIfNoRelatedNativeAssetFound() throws JsonProce contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -1287,7 +1300,7 @@ public void shouldReturnEmptyAssetIfIdIsNotPresentRelatedNativeAssetFound() thro contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -1345,7 +1358,7 @@ public void shouldReturnEmptyAssetIfDataTypeIsEmpty() throws JsonProcessingExcep contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -1380,7 +1393,7 @@ public void shouldSetBidAdmToNullIfCacheIdIsPresentAndReturnCreativeBidsIsFalse( final BidRequestCacheInfo cacheInfo = BidRequestCacheInfo.builder().doCaching(true).build(); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1410,7 +1423,7 @@ public void shouldSetBidAdmToNullIfVideoCacheIdIsPresentAndReturnCreativeVideoBi givenCacheServiceResult(singletonList(CacheInfo.of("id", null, null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1440,7 +1453,7 @@ public void shouldSetBidExpWhenCacheIdIsMatched() { final BidRequestCacheInfo cacheInfo = BidRequestCacheInfo.builder().doCaching(true).build(); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1470,7 +1483,7 @@ public void shouldSetBidExpMaxTtlWhenCacheIdIsMatchedAndBothTtlIsSet() { final BidRequestCacheInfo cacheInfo = BidRequestCacheInfo.builder().doCaching(true).build(); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1493,7 +1506,7 @@ public void shouldTolerateMissingExtInSeatBidAndBid() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then final ObjectNode expectedBidExt = mapper.createObjectNode(); @@ -1524,7 +1537,7 @@ public void shouldPassPreferDealsToWinningComparatorFactoryWhenBidRequestTrue() contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then verify(winningBidComparatorFactory, times(2)).create(eq(true)); @@ -1544,7 +1557,7 @@ public void shouldPassPreferDealsFalseWhenBidRequestPreferDealsIsNotDefined() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then verify(winningBidComparatorFactory, times(2)).create(eq(false)); @@ -1565,7 +1578,7 @@ public void shouldPopulateTargetingKeywords() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1601,7 +1614,7 @@ public void shouldPopulateTargetingKeywordsForAmpRequest() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1635,6 +1648,7 @@ public void shouldTruncateTargetingKeywordsByGlobalConfig() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); target = new BidResponseCreator( + 0, coreCacheService, bidderCatalog, vastModifier, @@ -1647,11 +1661,12 @@ public void shouldTruncateTargetingKeywordsByGlobalConfig() { 20, clock, jacksonMapper, + metrics, mediaTypeCacheTtl, cacheDefaultProperties); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1692,7 +1707,7 @@ public void shouldTruncateTargetingKeywordsByAccountConfig() { .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1741,7 +1756,7 @@ public void shouldTruncateTargetingKeywordsByRequestPassedValue() { .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -1786,7 +1801,7 @@ public void shouldReduceAndNotPopulateTargetingKeywordsForExtraBidsWhenCodePrefi contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse result = target.create(auctionContext, CACHE_INFO, multiBidMap).result(); + final BidResponse result = target.create(auctionContext, CACHE_INFO, aliases, multiBidMap).result(); // then assertThat(result.getSeatbid()) @@ -1840,7 +1855,7 @@ public void shouldNotPopulateTargetingKeywordsForExtraBidsWhenCodePrefixIsDefine contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse result = target.create(auctionContext, CACHE_INFO, multiBidMap).result(); + final BidResponse result = target.create(auctionContext, CACHE_INFO, aliases, multiBidMap).result(); final Map expectedWinningBidTargetingMap = new HashMap<>(); expectedWinningBidTargetingMap.put("hb_pb", "4.50"); @@ -1890,7 +1905,7 @@ public void shouldReduceAndPopulateTargetingKeywordsForExtraBidsWhenCodePrefixIs contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse result = target.create(auctionContext, CACHE_INFO, multiBidMap).result(); + final BidResponse result = target.create(auctionContext, CACHE_INFO, aliases, multiBidMap).result(); // then final Map bidder1Bid4Targeting = new HashMap<>(); @@ -1951,7 +1966,7 @@ public void shouldPopulateTargetingKeywordsForWinningBidsAndWinningBidsByBidder( contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -2002,7 +2017,7 @@ public void shouldPopulateTargetingKeywordsFromMediaTypePriceGranularities() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -2038,7 +2053,7 @@ public void shouldPopulateCacheIdHostPathAndUuidTargetingKeywords() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId", "videoId", null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -2083,7 +2098,7 @@ public void shouldPopulateTargetingKeywordsWithAdditionalValuesFromRequest() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -2116,7 +2131,7 @@ public void shouldPopulateTargetingKeywordsIfBidWasCachedAndAdmWasRemoved() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId", null, null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // Check if you didn't lost any bids because of bid change in winningBids set // then @@ -2164,7 +2179,7 @@ public void shouldAddExtPrebidEventsIfEventsAreEnabledAndExtRequestPrebidEventPr givenCacheServiceResult(emptyList()); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ArgumentCaptor> bidsArgumentCaptor = ArgumentCaptor.forClass(List.class); @@ -2218,7 +2233,7 @@ public void shouldAddExtPrebidEventsIfEventsAreEnabledAndAccountSupportEventsFor .willReturn(events); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2268,7 +2283,7 @@ public void shouldAddExtPrebidEventsIfEventsAreEnabledAndDefaultAccountAnalytics .willReturn(events); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2301,7 +2316,7 @@ public void shouldAddExtPrebidVideoToExtBidPrebidWhenVideoBids() { givenCacheServiceResult(emptyList()); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ArgumentCaptor> bidsArgumentCaptor = ArgumentCaptor.forClass(List.class); @@ -2336,7 +2351,7 @@ public void shouldAddDealTierSatisfiedToExtBidPrebidWhenBidsPrioritySatisfiedMin bidderResponses, emptyList()))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2372,7 +2387,7 @@ public void shouldNotAddExtPrebidEventsIfEventsAreNotEnabled() { .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2406,7 +2421,7 @@ public void shouldNotAddExtPrebidEventsIfExtRequestPrebidEventsNull() { .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2447,7 +2462,7 @@ public void shouldNotAddExtPrebidEventsIfAccountDoesNotSupportEventsForChannel() .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).hasSize(1) @@ -2479,7 +2494,7 @@ public void shouldReturnCacheEntityInExt() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId", "videoId", null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -2525,7 +2540,7 @@ public void shouldNotPopulateWinningBidTargetingIfIncludeWinnersFlagIsFalse() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId", "videoId", null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).flatExtracting(SeatBid::getBid) @@ -2571,7 +2586,7 @@ public void shouldNotPopulateBidderKeysTargetingIfIncludeBidderKeysFlagIsFalse() givenCacheServiceResult(singletonList(CacheInfo.of("cacheId", "videoId", null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).flatExtracting(SeatBid::getBid) @@ -2609,7 +2624,7 @@ public void shouldNotPopulateCacheIdTargetingKeywordsIfBidCpmIsZero() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId2", null, null, null))); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()).flatExtracting(SeatBid::getBid).hasSize(2) @@ -2646,7 +2661,7 @@ public void shouldNotCacheNonDealBidWithCpmIsZeroAndCacheDealBidWithZeroCpm() { givenCacheServiceResult(singletonList(CacheInfo.of("cacheId2", null, null, null))); // when - target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ArgumentCaptor> bidsArgumentCaptor = ArgumentCaptor.forClass(List.class); @@ -2687,7 +2702,7 @@ public void shouldPopulateBidResponseExtension() { new RuntimeException("cacheError"), emptyMap())); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ExtBidResponse responseExt = bidResponse.getExt(); @@ -2749,7 +2764,7 @@ public void shouldPopulateBidExpBasedOnCachedResult() { given(mediaTypeCacheTtl.getBannerCacheTtl()).willReturn(50); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -2801,7 +2816,7 @@ public void impToStoredVideoJsonShouldTolerateWhenStoredVideoFetchIsFailed() { Future.failedFuture("Fetch failed")); // when - final Future result = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future result = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // then verify(storedRequestProcessor).videoStoredDataResult(any(), eq(singletonList(imp)), anyList(), eq(timeout)); @@ -2858,7 +2873,7 @@ public void impToStoredVideoJsonShouldInjectStoredVideoWhenExtOptionsIsTrueAndVi givenCacheServiceResult(emptyList()); // when - final Future result = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future result = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then verify(storedRequestProcessor).videoStoredDataResult(any(), eq(asList(imp1, imp3)), anyList(), @@ -2903,7 +2918,7 @@ public void impToStoredVideoJsonShouldAddErrorsWithPrebidBidderWhenStoredVideoRe .willReturn(Future.failedFuture("Bad timeout")); // when - final Future result = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future result = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // then verify(storedRequestProcessor).videoStoredDataResult(any(), eq(singletonList(imp1)), anyList(), eq(timeout)); @@ -2942,7 +2957,7 @@ public void shouldProcessRequestAndAddErrorAboutDeprecatedBidder() { "invalid has been deprecated and is no longer available. Use valid instead."); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getExt()).isEqualTo( @@ -2973,7 +2988,7 @@ public void shouldProcessRequestAndAddErrorFromAuctionContext() { .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final Future result = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future result = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // then assertThat(result.result().getExt()).isEqualTo( @@ -3028,7 +3043,7 @@ public void shouldPopulateResponseDebugExtensionAndWarningsIfDebugIsEnabled() { .build())); // when - final BidResponse bidResponse = target.create(auctionContext, cacheInfo, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS).result(); // then final ExtBidResponse responseExt = bidResponse.getExt(); @@ -3090,7 +3105,7 @@ public void shouldPassIntegrationToCacheServiceAndBidEvents() { "http://imp-url?param=value&int=integration")); // when - final Future result = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future result = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then verify(coreCacheService).cacheBidsOpenrtb(anyList(), any(), any(), @@ -3132,7 +3147,7 @@ public void shouldPopulateActivityInfrastructureTraceLogOnSpecifiedTraceLevel() .auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse) @@ -3166,7 +3181,7 @@ public void shouldPopulateBidResponseExtErrorIfImpExtIsInvalid() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getExt().getErrors().get("prebid").getFirst().getMessage()).isEqualTo(errorMessage); @@ -3186,7 +3201,7 @@ public void shouldThrowErrorIfBidIdAndCorrespondingImpIdNotEquals() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final Future bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // when assertThat(bidResponse.failed()).isTrue(); @@ -3215,7 +3230,7 @@ public void shouldThrowExceptionWhenBidAdmIsParsedButImpNativeNotFound() throws contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getExt()) @@ -3256,7 +3271,7 @@ public void shouldThrowExceptionWhenNativeRequestIsInvalid() throws JsonProcessi contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getExt()) @@ -3295,7 +3310,7 @@ public void shouldPopulateBidAdmIfResponseAssetsIsNull() throws JsonProcessingEx contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse) @@ -3342,7 +3357,7 @@ public void shouldPopulateEventsContextForRequestIfEventsEnabledForRequest() { .willReturn(givenEvents); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -3371,7 +3386,7 @@ public void shouldNotPopulateBidExtTargetingWhenExtRequestTargetingPricegranular contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then final ObjectNode givenDefaultBidExt = @@ -3406,7 +3421,7 @@ public void shouldNotPopulateBidExtTargetingWhenExtRequestTargetingSettingsIsFal contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then final ObjectNode givenDefaultBidExt = @@ -3432,7 +3447,7 @@ public void shouldCopyRequestExtPrebidPassThroughToResponseExtPrebidPassThroughW contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getExt()) @@ -3463,7 +3478,7 @@ public void shouldCopyImpExtPrebidPassThroughToResponseBidExtPrebidPassThroughWh contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -3476,6 +3491,7 @@ public void shouldCopyImpExtPrebidPassThroughToResponseBidExtPrebidPassThroughWh @Test public void shouldAddExtPrebidFledgeIfAvailable() { // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); final Imp imp = givenImp("i1").toBuilder() .ext(mapper.createObjectNode().put("ae", 1)) .build(); @@ -3495,7 +3511,7 @@ public void shouldAddExtPrebidFledgeIfAvailable() { // when final BidResponse bidResponse = target - .create(auctionContext, CACHE_INFO, MULTI_BIDS) + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) .result(); // then @@ -3505,13 +3521,440 @@ public void shouldAddExtPrebidFledgeIfAvailable() { .usingRecursiveComparison() .isEqualTo(fledgeAuctionConfig.toBuilder() .bidder("bidder1") - .adapter("bidder1") + .adapter("adapter1") + .build()); + } + + @Test + public void shouldAddExtIgiIfAvailableAndExtRequestPrebidPaaFormatIsIab() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("i1").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.IAB), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("i1").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt().getIgi()).containsExactly( + ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .ext(ExtIgiIgsExt.of("bidder1", "adapter1")) + .build())) .build()); } + @Test + public void shouldAddExtPrebidFledgeIfAvailableAndExtRequestPrebidPaaFormatIsOriginal() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.ORIGINAL), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getPrebid) + .extracting(ExtBidResponsePrebid::getFledge) + .extracting(ExtBidResponseFledge::getAuctionConfigs) + .asList() + .containsExactly( + FledgeAuctionConfig.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .bidder("bidder1") + .adapter("adapter1") + .build()); + } + + @Test + public void shouldAddExtIgiIfAvailableAndExtRequestPrebidPaaFormatIsAbsentAndAccountConfigPaaFormatSetToIab() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), identity(), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .auctionParticipations(toAuctionParticipant(bidderResponses)) + .account(Account.builder() + .auction(AccountAuctionConfig.builder().paaFormat(PaaFormat.IAB).build()) + .build())); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt().getIgi()).containsExactly( + ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .ext(ExtIgiIgsExt.of("bidder1", "adapter1")) + .build())) + .build()); + } + + @Test + public void shouldAddExtPrebidFledgeIfAvailableAndRequestPaaFormatIsAbsentAndAccountConfigPaaFormatSetToOriginal() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), identity(), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .auctionParticipations(toAuctionParticipant(bidderResponses)) + .account(Account.builder() + .auction(AccountAuctionConfig.builder().paaFormat(PaaFormat.ORIGINAL).build()) + .build())); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getPrebid) + .extracting(ExtBidResponsePrebid::getFledge) + .extracting(ExtBidResponseFledge::getAuctionConfigs) + .asList() + .containsExactly( + FledgeAuctionConfig.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .bidder("bidder1") + .adapter("adapter1") + .build()); + } + + @Test + public void shouldDefaultToOriginalPaaFormat() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), identity(), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getPrebid) + .extracting(ExtBidResponsePrebid::getFledge) + .extracting(ExtBidResponseFledge::getAuctionConfigs) + .asList() + .containsExactly( + FledgeAuctionConfig.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .bidder("bidder1") + .adapter("adapter1") + .build()); + } + + @Test + public void shouldDropExtIgiIgbIfAvailableAndExtIgiImpIdIsAbsent() { + // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.IAB), imp); + final ExtIgi igi = ExtIgi.builder() + .igs(singletonList(ExtIgiIgs.builder().impId("impId").config(mapper.createObjectNode()).build())) + .igb(singletonList(ExtIgiIgb.builder().build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .debugContext(DebugContext.of(true, false, null)) + .auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getIgi) + .asList() + .containsExactly( + ExtIgi.builder() + .igs(singletonList( + ExtIgiIgs.builder() + .impId("impId") + .config(mapper.createObjectNode()) + .ext(ExtIgiIgsExt.of("bidder1", "adapter1")) + .build())) + .build()); + + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getWarnings) + .extracting(warnings -> warnings.get(PREBID)) + .asList() + .containsExactly( + ExtBidderError.of( + BidderError.Type.generic.getCode(), + "ExtIgi with absent impId from bidder: bidder1")); + verify(metrics).updateAlertsMetrics(MetricName.general); + } + + @Test + public void shouldDropExtIgiIgsIfAvailableAndExtIgiIgsImpIdIsAbsent() { + // given + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.IAB), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().config(mapper.createObjectNode()).build())) + .igb(singletonList(ExtIgiIgb.builder().build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .debugContext(DebugContext.of(true, false, null)) + .auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getIgi) + .asList() + .containsExactly( + ExtIgi.builder() + .impid("impId") + .igb(singletonList(ExtIgiIgb.builder().build())) + .build()); + + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getWarnings) + .extracting(warnings -> warnings.get(PREBID)) + .asList() + .containsExactly( + ExtBidderError.of( + BidderError.Type.generic.getCode(), + "ExtIgiIgs with absent impId from bidder: bidder1")); + verify(metrics).updateAlertsMetrics(MetricName.general); + } + + @Test + public void shouldDropExtIgiIgsIfAvailableAndExtIgiIgsConfigIsAbsent() { + // given + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.IAB), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder().impId("impId").build())) + .igb(singletonList(ExtIgiIgb.builder().build())) + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .debugContext(DebugContext.of(true, false, null)) + .auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getIgi) + .asList() + .containsExactly( + ExtIgi.builder() + .impid("impId") + .igb(singletonList(ExtIgiIgb.builder().build())) + .build()); + + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getWarnings) + .extracting(warnings -> warnings.get(PREBID)) + .asList() + .containsExactly( + ExtBidderError.of( + BidderError.Type.generic.getCode(), + "ExtIgiIgs with absent config from bidder: bidder1")); + verify(metrics).updateAlertsMetrics(MetricName.general); + } + + @Test + public void shouldDropExtIgiIfAvailableAndExtIgiIgsAndExtIgiIgbAreAbsent() { + // given + final Imp imp = givenImp("impId").toBuilder() + .ext(mapper.createObjectNode().put("ae", 1)) + .build(); + final BidRequest bidRequest = givenBidRequest(identity(), ext -> ext.paaFormat(PaaFormat.IAB), imp); + final ExtIgi igi = ExtIgi.builder() + .impid("impId") + .build(); + + final Bid bid = Bid.builder().id("bidId1").price(BigDecimal.valueOf(2.37)).impid("impId").build(); + final List bidderResponses = singletonList( + BidderResponse.of("bidder1", + BidderSeatBid.builder() + .bids(List.of(BidderBid.of(bid, banner, "USD"))) + .igi(singletonList(igi)) + .build(), 100)); + + final AuctionContext auctionContext = givenAuctionContext( + bidRequest, + contextBuilder -> contextBuilder + .debugContext(DebugContext.of(true, false, null)) + .auctionParticipations(toAuctionParticipant(bidderResponses))); + + // when + final BidResponse bidResponse = target + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) + .result(); + + // then + assertThat(bidResponse.getExt()) + .extracting(ExtBidResponse::getIgi) + .isNull(); + } + @Test public void shouldAddExtPrebidFledgeIfAvailableEvenIfBidsEmpty() { // given + given(bidderCatalog.resolveBaseBidder("bidder1")).willReturn("adapter1"); final Imp imp = givenImp("i1").toBuilder() .ext(mapper.createObjectNode().put("ae", 1)) .build(); @@ -3530,7 +3973,7 @@ public void shouldAddExtPrebidFledgeIfAvailableEvenIfBidsEmpty() { // when final BidResponse bidResponse = target - .create(auctionContext, CACHE_INFO, MULTI_BIDS) + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) .result(); // then @@ -3540,7 +3983,7 @@ public void shouldAddExtPrebidFledgeIfAvailableEvenIfBidsEmpty() { .usingRecursiveComparison() .isEqualTo(fledgeAuctionConfig.toBuilder() .bidder("bidder1") - .adapter("bidder1") + .adapter("adapter1") .build()); } @@ -3563,7 +4006,7 @@ public void shouldDropFledgeResponsesReferencingUnknownImps() { // when final BidResponse bidResponse = target - .create(auctionContext, CACHE_INFO, MULTI_BIDS) + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) .result(); // then @@ -3595,7 +4038,7 @@ public void shouldPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsTrue() // when final BidResponse bidResponse = target - .create(auctionContext, CACHE_INFO, MULTI_BIDS) + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) .result(); // then @@ -3630,7 +4073,7 @@ public void shouldNotPopulateExtPrebidSeatNonBidWhenReturnAllBidStatusFlagIsFals // when final BidResponse bidResponse = target - .create(auctionContext, CACHE_INFO, MULTI_BIDS) + .create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS) .result(); // then @@ -3672,7 +4115,7 @@ public void shouldPopulateBidExtWhenExtMediaTypePriceGranularityHasValidVideoExt contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -3715,7 +4158,7 @@ public void shouldPopulateBidExtWhenExtMediaTypePriceGranularityHasValidxNativeE context -> context.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -3761,7 +4204,7 @@ public void shouldThrowErrorIfExtMediaTypePriceGranularityCannotBeParsed() { contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final Future bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS); + final Future bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS); // then assertThat(bidResponse.failed()).isTrue(); @@ -3786,7 +4229,7 @@ public void shouldPopulateTargetingKeywordsWithDefaultPrefixAndRelatedWarning() contextBuilder -> contextBuilder.auctionParticipations(toAuctionParticipant(bidderResponses))); // when - final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, MULTI_BIDS).result(); + final BidResponse bidResponse = target.create(auctionContext, CACHE_INFO, aliases, MULTI_BIDS).result(); // then assertThat(bidResponse.getSeatbid()) @@ -3850,7 +4293,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromBid() { given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -3919,7 +4362,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromImp() { given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -3988,7 +4431,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromRequest() { given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4057,7 +4500,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromAccountBanne given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4126,7 +4569,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromAccountVideo given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4195,7 +4638,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromMediaTypeTtl given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4264,7 +4707,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetTtlFromMediaTypeTtl given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4333,7 +4776,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetDefaultTtlForBanner given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4402,7 +4845,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetDefaultTtlForVideoB given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4471,7 +4914,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetDefaultTtlForAudioB given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4540,7 +4983,7 @@ public void createShouldSendCacheRequestWithExpectedTtlAndSetDefaultTtlForNative given(cacheDefaultProperties.getNativeTtl()).willReturn(63); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4599,7 +5042,7 @@ public void createShouldSendCacheRequestWithTtlFromMediaTypeWhenAccountIsEmpty() given(mediaTypeCacheTtl.getBannerCacheTtl()).willReturn(50); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4657,7 +5100,7 @@ public void createShouldSendCacheRequestWithNoTtlAndSetEmptyTtl() { given(mediaTypeCacheTtl.getBannerCacheTtl()).willReturn(null); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(CacheContext.class); @@ -4715,7 +5158,7 @@ public void createShouldSendCacheRequestWithVideoBidWithTtlMaxOfTtlAndVideoTtl() given(mediaTypeCacheTtl.getBannerCacheTtl()).willReturn(null); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then assertThat(response.succeeded()).isTrue(); @@ -4774,7 +5217,7 @@ public void createShouldSendCacheRequestWithBannerBidWithTtlMaxOfTtlAndVideoTtl( given(mediaTypeCacheTtl.getBannerCacheTtl()).willReturn(null); // when - final Future response = target.create(auctionContext, cacheInfo, MULTI_BIDS); + final Future response = target.create(auctionContext, cacheInfo, aliases, MULTI_BIDS); // then assertThat(response.succeeded()).isTrue(); @@ -4865,7 +5308,7 @@ private AuctionContext givenAuctionContext(BidRequest bidRequest, .timeoutContext(TimeoutContext.of(0, timeout, 0)) .debugContext(DebugContext.empty()) .debugHttpCalls(new HashMap<>()) - .debugWarnings(emptyList()) + .debugWarnings(new ArrayList<>()) .auctionParticipations(emptyList()) .bidRejectionTrackers(new HashMap<>()) .prebidErrors(new ArrayList<>()); @@ -5021,6 +5464,7 @@ private static ExtBidPrebid toExtBidPrebid(ObjectNode ext) { private BidResponseCreator givenBidResponseCreator(int truncateAttrChars) { return new BidResponseCreator( + 0, coreCacheService, bidderCatalog, vastModifier, @@ -5033,6 +5477,7 @@ private BidResponseCreator givenBidResponseCreator(int truncateAttrChars) { truncateAttrChars, clock, jacksonMapper, + metrics, mediaTypeCacheTtl, cacheDefaultProperties); } diff --git a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java index 72ea7a78bbe..2e8c9a5e2c1 100644 --- a/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java +++ b/src/test/java/org/prebid/server/auction/ExchangeServiceTest.java @@ -287,7 +287,7 @@ public class ExchangeServiceTest extends VertxTest { @SuppressWarnings("unchecked") @BeforeEach public void setUp() { - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture(givenBidResponseWithBids(singletonList(givenBid(identity()))))); given(bidderCatalog.isValidName(anyString())).willReturn(true); @@ -1269,7 +1269,7 @@ public void shouldReturnSeparateSeatBidsForTheSameBidderIfBiddersAliasAndBidderW .auctiontimestamp(1000L) .build()))); - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture(BidResponse.builder() .seatbid(asList( givenSeatBid(singletonList(givenBid(identity())), identity()), @@ -1350,7 +1350,7 @@ public void shouldOverrideDebugEnabledFlag() { .httpCalls(singletonList(ExtHttpCall.builder().build())) .build())); - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture( BidResponse.builder() .ext(ExtBidResponse.builder() @@ -1365,7 +1365,7 @@ public void shouldOverrideDebugEnabledFlag() { verify(httpBidderRequester).requestBids(any(), any(), any(), any(), any(), any(), eq(true)); final ArgumentCaptor captor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(captor.capture(), any(), anyMap()); + verify(bidResponseCreator).create(captor.capture(), any(), any(), anyMap()); assertThat(captor.getValue().getDebugContext()).isEqualTo(DebugContext.of(true, true, null)); assertThat(result.getBidResponse().getExt().getDebug()).isNotNull(); @@ -1380,7 +1380,7 @@ public void shouldAddDebugInfoIfDebugEnabledAndPublisherAndBidderAllowedDebug() given(httpBidderRequester.requestBids(any(), any(), any(), any(), any(), any(), eq(true))) .willReturn(Future.succeededFuture(bidderSeatBid)); - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture( BidResponse.builder() .ext(ExtBidResponse.builder() @@ -1402,7 +1402,7 @@ public void shouldAddDebugInfoIfDebugEnabledAndPublisherAndBidderAllowedDebug() verify(httpBidderRequester).requestBids(any(), any(), any(), any(), any(), any(), eq(true)); final ArgumentCaptor captor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(captor.capture(), any(), anyMap()); + verify(bidResponseCreator).create(captor.capture(), any(), any(), anyMap()); assertThat(captor.getValue().getDebugContext()) .isEqualTo(DebugContext.of(true, true, null)); @@ -1423,7 +1423,7 @@ public void shouldNotAddDebugInfoIfPublisherIsNotAllowedToDebug() { given(debugResolver.resolveDebugForBidder(any(), eq("bidder"))) .willReturn(false); - given(bidResponseCreator.create(any(), any(), any())).willReturn( + given(bidResponseCreator.create(any(), any(), any(), any())).willReturn( Future.succeededFuture(BidResponse.builder().ext(ExtBidResponse.builder().build()).build())); // when @@ -1433,7 +1433,7 @@ public void shouldNotAddDebugInfoIfPublisherIsNotAllowedToDebug() { verify(httpBidderRequester).requestBids(any(), any(), any(), any(), any(), any(), eq(false)); final ArgumentCaptor captor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(captor.capture(), any(), anyMap()); + verify(bidResponseCreator).create(captor.capture(), any(), any(), anyMap()); assertThat(captor.getValue().getDebugContext()).isEqualTo( DebugContext.of(false, false, null)); @@ -1454,7 +1454,7 @@ public void shouldNotAddDebugInfoIfBidderDisabledDebug() { given(debugResolver.resolveDebugForBidder(any(), eq("bidder"))) .willReturn(false); - given(bidResponseCreator.create(any(), any(), any())).willReturn( + given(bidResponseCreator.create(any(), any(), any(), any())).willReturn( Future.succeededFuture(BidResponse.builder().ext(ExtBidResponse.builder().build()).build())); // when @@ -1464,7 +1464,7 @@ public void shouldNotAddDebugInfoIfBidderDisabledDebug() { verify(httpBidderRequester).requestBids(any(), any(), any(), any(), any(), any(), eq(false)); final ArgumentCaptor captor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(captor.capture(), any(), anyMap()); + verify(bidResponseCreator).create(captor.capture(), any(), any(), anyMap()); assertThat(captor.getValue().getDebugContext()).isEqualTo( DebugContext.of(true, true, null)); @@ -1543,7 +1543,7 @@ public void shouldCallBidResponseCreatorWithExpectedParamsAndUpdateDebugErrors() final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(AuctionContext.class); verify(bidResponseCreator) - .create(contextArgumentCaptor.capture(), eq(expectedCacheInfo), eq(expectedMultiBidMap)); + .create(contextArgumentCaptor.capture(), eq(expectedCacheInfo), any(), eq(expectedMultiBidMap)); final Bid expectedThirdBid = Bid.builder() .id("bidId3") @@ -1610,6 +1610,7 @@ public void shouldCallBidResponseCreatorWithWinningOnlyTrueWhenIncludeBidderKeys verify(bidResponseCreator).create( auctionContextArgumentCaptor.capture(), eq(BidRequestCacheInfo.builder().doCaching(true).shouldCacheWinningBidsOnly(true).build()), + any(), eq(emptyMap())); assertThat(singletonList(auctionContextArgumentCaptor.getValue().getBidRequest())) @@ -1644,7 +1645,7 @@ public void shouldCallBidResponseCreatorWithWinningOnlyFalseWhenWinningOnlyIsNul target.holdAuction(givenRequestContext(bidRequest)); // then - verify(bidResponseCreator).create(any(), eq(BidRequestCacheInfo.builder().build()), eq(emptyMap())); + verify(bidResponseCreator).create(any(), eq(BidRequestCacheInfo.builder().build()), any(), eq(emptyMap())); } @Test @@ -3723,7 +3724,7 @@ public void shouldReduceBidsHavingDealIdWithSameImpIdByBidderWithToleratingNotOb // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any()); + verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any(), any()); assertThat(contextArgumentCaptor.getValue().getAuctionParticipations()).hasSize(1) .extracting(AuctionParticipation::getBidderResponse) .extracting(BidderResponse::getSeatBid) @@ -3755,7 +3756,7 @@ public void shouldReduceBidsHavingDealIdWithSameImpIdByBidderWithToleratingNotOb // then final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any()); + verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any(), any()); assertThat(contextArgumentCaptor.getValue().getAuctionParticipations()).hasSize(1) .extracting(AuctionParticipation::getBidderResponse) @@ -3784,6 +3785,7 @@ public void shouldResponseWithEmptySeatBidIfBidderNotSupportProvidedMediaTypes() .build(), 0))), any(), + any(), any())) .willReturn(Future.succeededFuture(BidResponse.builder().id("uniqId").build())); @@ -3836,6 +3838,7 @@ public void shouldResponseWithEmptySeatBidIfBidderNotSupportRequestCurrency() { .build(), 0))), any(), + any(), any())) .willReturn(Future.succeededFuture(BidResponse.builder().id("uniqId").build())); @@ -4057,7 +4060,7 @@ private BidRequest captureBidRequest() { private List captureAuctionParticipations() { final ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(AuctionContext.class); - verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any()); + verify(bidResponseCreator).create(contextArgumentCaptor.capture(), any(), any(), any()); return contextArgumentCaptor.getValue().getAuctionParticipations(); } @@ -4161,12 +4164,12 @@ private static ExtRequestTargeting givenTargeting(boolean includebidderkeys) { } private void givenBidResponseCreator(List bids) { - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture(givenBidResponseWithBids(bids))); } private void givenBidResponseCreator(Map> errors) { - given(bidResponseCreator.create(any(), any(), any())) + given(bidResponseCreator.create(any(), any(), any(), any())) .willReturn(Future.succeededFuture(givenBidResponseWithError(errors))); } diff --git a/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java b/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java index 717ca62dedd..30148ed35b9 100644 --- a/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java +++ b/src/test/java/org/prebid/server/auction/ImpAdjusterTest.java @@ -1,6 +1,7 @@ package org.prebid.server.auction; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.Deal; import com.iab.openrtb.request.Imp; @@ -77,6 +78,79 @@ public void adjustShouldReturnOriginalImpWhenImpExtPrebidImpIsAbsent() { assertThat(debugMessages).isEmpty(); } + @Test + public void adjustShouldSetImpExtIgsAeWhenImpExtAeIsZero() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("ae", IntNode.valueOf(0)); + + final Imp givenImp = Imp.builder().ext(ext).build(); + + final List debugMessages = new ArrayList<>(); + + // when + final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + + // then + assertThat(result.getExt().get("igs").get(0).get("ae")).isEqualTo(IntNode.valueOf(0)); + assertThat(debugMessages).isEmpty(); + } + + @Test + public void adjustShouldSetImpExtIgsAeWhenImpExtAeIsOne() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("ae", IntNode.valueOf(1)); + + final Imp givenImp = Imp.builder().ext(ext).build(); + + final List debugMessages = new ArrayList<>(); + + // when + final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + + // then + assertThat(result.getExt().get("igs").get(0).get("ae")).isEqualTo(IntNode.valueOf(1)); + assertThat(debugMessages).isEmpty(); + } + + @Test + public void adjustShouldNotSetImpExtIgsAeWhenImpExtAeIsNotZeroOrOne() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("ae", IntNode.valueOf(3)); + + final Imp givenImp = Imp.builder().ext(ext).build(); + + final List debugMessages = new ArrayList<>(); + + // when + final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + + // then + assertThat(result.getExt().get("igs")).isNull(); + assertThat(debugMessages).isEmpty(); + } + + @Test + public void adjustShouldNotModifyImpExtIgsAeWhenImpExtIgsAePresent() { + // given + final ObjectNode ext = mapper.createObjectNode(); + ext.set("ae", IntNode.valueOf(0)); + ext.set("igs", mapper.createArrayNode().add(mapper.createObjectNode().set("ae", IntNode.valueOf(123)))); + + final Imp givenImp = Imp.builder().ext(ext).build(); + + final List debugMessages = new ArrayList<>(); + + // when + final Imp result = target.adjust(givenImp, "someBidder", bidderAliases, debugMessages); + + // then + assertThat(result.getExt().get("igs").get(0).get("ae")).isEqualTo(IntNode.valueOf(123)); + assertThat(debugMessages).isEmpty(); + } + @Test public void adjustShouldRemoveExpImpFromOriginalImpWhenImpExtPrebidImpHasEmptyBidder() { // given diff --git a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java index d2ce145e7ef..2b067ab06d8 100644 --- a/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java +++ b/src/test/java/org/prebid/server/bidder/HttpBidderRequesterTest.java @@ -37,6 +37,8 @@ import org.prebid.server.execution.timeout.TimeoutFactory; import org.prebid.server.model.CaseInsensitiveMultiMap; import org.prebid.server.proto.openrtb.ext.response.ExtHttpCall; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; import org.prebid.server.util.HttpUtil; import org.prebid.server.vertx.httpclient.HttpClient; @@ -196,7 +198,7 @@ public void shouldPassStoredResponseToBidderMakeBidsMethodAndReturnSeatBids() { emptyList())); final List bids = asList(BidderBid.of(null, null, null), BidderBid.of(null, null, null)); - given(bidder.makeBidderResponse(any(), any())).willReturn(CompositeBidderResponse.withBids(bids, emptyList())); + given(bidder.makeBidderResponse(any(), any())).willReturn(CompositeBidderResponse.builder().bids(bids).build()); final BidderRequest bidderRequest = BidderRequest.builder() .bidder("bidder") @@ -346,7 +348,7 @@ public void shouldReturnBidsCreatedByBidder() { givenSuccessfulBidderMakeHttpRequests(); final List bids = asList(BidderBid.of(null, null, null), BidderBid.of(null, null, null)); - given(bidder.makeBidderResponse(any(), any())).willReturn(CompositeBidderResponse.withBids(bids, emptyList())); + given(bidder.makeBidderResponse(any(), any())).willReturn(CompositeBidderResponse.builder().bids(bids).build()); final BidderRequest bidderRequest = BidderRequest.builder() .bidder("bidder") @@ -379,8 +381,7 @@ public void shouldReturnBidsCreatedByMakeBids() { givenSuccessfulBidderMakeHttpRequests(); final List bids = emptyList(); - given(bidder.makeBidderResponse(any(), any())) - .willReturn(CompositeBidderResponse.withBids(bids, null)); + given(bidder.makeBidderResponse(any(), any())).willReturn(CompositeBidderResponse.builder().bids(bids).build()); final BidderRequest bidderRequest = BidderRequest.builder() .bidder("bidder") @@ -417,8 +418,11 @@ public void shouldReturnFledgeCreatedByBidder() { givenFledgeAuctionConfig("imp-2")); final List bids = emptyList(); - given(bidder.makeBidderResponse(any(), any())) - .willReturn(CompositeBidderResponse.withBids(bids, fledgeAuctionConfigs)); + given(bidder.makeBidderResponse(any(), any())).willReturn( + CompositeBidderResponse.builder() + .bids(bids) + .fledgeAuctionConfigs(fledgeAuctionConfigs) + .build()); final BidderRequest bidderRequest = BidderRequest.builder() .bidder("bidder") @@ -446,6 +450,53 @@ public void shouldReturnFledgeCreatedByBidder() { verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); } + @Test + public void shouldReturnExtIgiCreatedByBidder() { + // given + givenSuccessfulBidderMakeHttpRequests(); + + final List igi = List.of( + ExtIgi.builder() + .impid("impId") + .igs(singletonList(ExtIgiIgs.builder() + .config(mapper.createObjectNode()) + .build())) + .build()); + + final List bids = emptyList(); + + given(bidder.makeBidderResponse(any(), any())).willReturn( + CompositeBidderResponse.builder() + .bids(bids) + .igi(igi) + .build()); + + final BidderRequest bidderRequest = BidderRequest.builder() + .bidder("bidder") + .bidRequest(BidRequest.builder().build()) + .build(); + + // when + final BidderSeatBid bidderSeatBid = + target + .requestBids( + bidder, + bidderRequest, + bidRejectionTracker, + timeout, + CaseInsensitiveMultiMap.empty(), + bidderAliases, + false) + .result(); + + // then + assertThat(bidderSeatBid.getBids()).hasSameElementsAs(bids); + assertThat(bidderSeatBid.getIgi()).containsExactlyElementsOf(igi); + + verify(bidRejectionTracker, never()).rejectImp(anyString(), any()); + verify(bidRejectionTracker, never()).rejectImps(anyList(), any()); + } + @Test public void shouldCompressRequestBodyIfContentEncodingHeaderIsGzip() { // given @@ -555,8 +606,8 @@ public void processBids(List bids) { final BidderBid bidderBidDeal1 = BidderBid.of(Bid.builder().impid("deal1").dealid("deal1").build(), null, null); final BidderBid bidderBidDeal2 = BidderBid.of(Bid.builder().impid("deal2").dealid("deal2").build(), null, null); given(bidder.makeBidderResponse(any(), any())).willReturn( - CompositeBidderResponse.withBids(singletonList(bidderBidDeal1), emptyList()), - CompositeBidderResponse.withBids(singletonList(bidderBidDeal2), emptyList())); + CompositeBidderResponse.builder().bids(singletonList(bidderBidDeal1)).build(), + CompositeBidderResponse.builder().bids(singletonList(bidderBidDeal2)).build()); // when final BidderSeatBid bidderSeatBid = target.requestBids( @@ -604,7 +655,7 @@ public void shouldFinishWhenAllDealRequestsAreFinishedAndNoDealsProvided() { final BidderBid bidderBid = BidderBid.of(Bid.builder().dealid("deal2").build(), null, null); given(bidder.makeBidderResponse(any(), any())).willReturn( - CompositeBidderResponse.withBids(singletonList(bidderBid), emptyList())); + CompositeBidderResponse.builder().bids(singletonList(bidderBid)).build()); // when final BidderSeatBid bidderSeatBid = @@ -737,7 +788,7 @@ public void shouldReturnRecordBidRejections() throws JsonProcessingException { .bid(Bid.builder().impid("2").build()) .build()); given(bidder.makeBidderResponse(any(), any())) - .willReturn(CompositeBidderResponse.withBids(secondRequestBids, null)); + .willReturn(CompositeBidderResponse.builder().bids(secondRequestBids).build()); final BidderRequest bidderRequest = BidderRequest.builder() .bidder("bidder") diff --git a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java index 09f2f113fb1..291dd35d415 100644 --- a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java @@ -20,7 +20,8 @@ import org.prebid.server.bidder.model.HttpResponse; import org.prebid.server.bidder.model.Result; import org.prebid.server.proto.openrtb.ext.response.BidType; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import java.util.List; import java.util.Set; @@ -199,7 +200,7 @@ public void makeBidderResponseShouldReturnBidWithNetworkNameFromExtPrebid() thro // given final BidderCall httpCall = givenHttpCall( givenBidResponse(bid -> bid - .impid("123") + .impid("123") .ext(givenBidExtWithNetwork("anyNetworkName")))); // when @@ -236,18 +237,16 @@ public void makeBidderResponseShouldReturnEmptyNetworkNameWhenBidExtPrebidNotCon @Test public void makeBidderResponseShouldReturnFledgeConfigs() throws JsonProcessingException { // given + final List igs = List.of( + ExtIgiIgs.builder().config(mapper.createObjectNode().put("proterty1", "value1")).build(), + ExtIgiIgs.builder().config(mapper.createObjectNode().put("proterty2", "value2")).build()); + final CriteoBidResponse bidResponseWithFledge = CriteoBidResponse.builder() .ext(CriteoExtBidResponse.of(List.of( - CriteoIgiExtBidResponse.of("imp_id1", List.of( - CriteoIgsIgiExtBidResponse.of( - mapper.createObjectNode().put("proterty1", "value1")), - CriteoIgsIgiExtBidResponse.of( - mapper.createObjectNode().put("proterty2", "value2")))), - CriteoIgiExtBidResponse.of("imp_id2", List.of( - CriteoIgsIgiExtBidResponse.of( - mapper.createObjectNode().put("proterty3", "value3")), - CriteoIgsIgiExtBidResponse.of( - mapper.createObjectNode().put("proterty4", "value4"))))))) + ExtIgi.builder() + .impid("imp_id1") + .igs(igs) + .build()))) .build(); final BidderCall httpCall = givenHttpCall(mapper.writeValueAsString(bidResponseWithFledge)); @@ -255,19 +254,12 @@ public void makeBidderResponseShouldReturnFledgeConfigs() throws JsonProcessingE final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then + final List expectedIgs = List.of( + ExtIgiIgs.builder().config(mapper.createObjectNode().put("proterty1", "value1")).build(), + ExtIgiIgs.builder().config(mapper.createObjectNode().put("proterty2", "value2")).build()); + assertThat(result.getErrors()).isEmpty(); - assertThat(result.getFledgeAuctionConfigs()) - .containsExactlyInAnyOrder( - FledgeAuctionConfig.builder() - .bidder("criteo") - .impId("imp_id1") - .config(mapper.createObjectNode().put("proterty1", "value1")) - .build(), - FledgeAuctionConfig.builder() - .bidder("criteo") - .impId("imp_id2") - .config(mapper.createObjectNode().put("proterty3", "value3")) - .build()); + assertThat(result.getIgi()).containsExactly(ExtIgi.builder().impid("imp_id1").igs(expectedIgs).build()); } @Test diff --git a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java index 35726f5a205..e91a0300df1 100644 --- a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java @@ -46,7 +46,8 @@ import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.version.PrebidVersionProvider; import java.util.List; @@ -771,14 +772,14 @@ public void makeBidderResponseShouldReturnFledgeAuctionConfig() throws JsonProce // given final String impId = "imp_id"; final BidResponse bidResponse = givenBidResponse(bidBuilder -> bidBuilder.impid(impId).mtype(1)); - final ObjectNode fledgeAuctionConfig = mapper.createObjectNode(); + final ObjectNode auctionConfig = mapper.createObjectNode(); final BidRequest bidRequest = BidRequest.builder() .imp(List.of(Imp.builder().id(impId).build())) .build(); final IxBidResponse bidResponseWithFledge = IxBidResponse.builder() .cur(bidResponse.getCur()) .seatbid(bidResponse.getSeatbid()) - .ext(IxExtBidResponse.of(List.of(AuctionConfigExtBidResponse.of(impId, fledgeAuctionConfig)))) + .ext(IxExtBidResponse.of(List.of(AuctionConfigExtBidResponse.of(impId, auctionConfig)))) .build(); final BidderCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString(bidResponseWithFledge)); @@ -787,14 +788,15 @@ public void makeBidderResponseShouldReturnFledgeAuctionConfig() throws JsonProce final CompositeBidderResponse result = target.makeBidderResponse(httpCall, bidRequest); // then + final ExtIgiIgs igs = ExtIgiIgs.builder() + .impId(impId) + .config(auctionConfig) + .build(); + assertThat(result.getErrors()).isEmpty(); assertThat(result.getBids()) .containsOnly(BidderBid.of(Bid.builder().impid(impId).mtype(1).build(), banner, bidResponse.getCur())); - final FledgeAuctionConfig expectedFledge = FledgeAuctionConfig.builder() - .impId(impId) - .config(fledgeAuctionConfig) - .build(); - assertThat(result.getFledgeAuctionConfigs()).containsExactly(expectedFledge); + assertThat(result.getIgi()).containsExactly(ExtIgi.builder().igs(singletonList(igs)).build()); } private static ExtRequest givenExtRequest(String pbjsv) { diff --git a/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java b/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java index 15e67bb5e25..03a3cba66f8 100644 --- a/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/medianet/MedianetBidderTest.java @@ -21,7 +21,8 @@ import org.prebid.server.bidder.model.HttpResponse; import org.prebid.server.bidder.model.Result; import org.prebid.server.proto.openrtb.ext.ExtPrebid; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import java.util.ArrayList; import java.util.List; @@ -215,13 +216,14 @@ public void makeBidsShouldReturnFledgeConfigIfBidIsPresent() throws JsonProcessi final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then + final ExtIgiIgs igs = ExtIgiIgs.builder() + .impId("imp_id") + .config(mapper.createObjectNode().put("someKey", "someValue")) + .build(); + assertThat(result.getErrors()).isEmpty(); assertThat(result.getBids()).hasSize(1); - assertThat(result.getFledgeAuctionConfigs()) - .containsOnly(FledgeAuctionConfig.builder() - .impId("imp_id") - .config(mapper.createObjectNode().put("someKey", "someValue")) - .build()); + assertThat(result.getIgi()).containsExactly(ExtIgi.builder().igs(singletonList(igs)).build()); } @Test @@ -235,13 +237,14 @@ public void makeBidsShouldReturnFledgeConfigIfBidIsAbsent() throws JsonProcessin final CompositeBidderResponse result = target.makeBidderResponse(httpCall, null); // then + final ExtIgiIgs igs = ExtIgiIgs.builder() + .impId("imp_id") + .config(mapper.createObjectNode().put("someKey", "someValue")) + .build(); + assertThat(result.getErrors()).isEmpty(); assertThat(result.getBids()).isEmpty(); - assertThat(result.getFledgeAuctionConfigs()) - .containsOnly(FledgeAuctionConfig.builder() - .impId("imp_id") - .config(mapper.createObjectNode().put("someKey", "someValue")) - .build()); + assertThat(result.getIgi()).containsExactly(ExtIgi.builder().igs(singletonList(igs)).build()); } private static MedianetBidResponse sampleBidResponse(FunctionThis is an Ad") .build(), BidType.banner, "UAH")); - assertThat(result.getFledgeAuctionConfigs()) - .containsOnly(FledgeAuctionConfig.builder() - .impId("impId1") - .config(mapper.createObjectNode().put("somevalue", 1)) - .build()); + assertThat(result.getIgi()).containsExactly(igi); } @Test @@ -753,13 +757,16 @@ public void makeBidsShouldReturnFledgeConfigEvenIfNoBids() throws JsonProcessing final CompositeBidderResponse result = target.makeBidderResponse(httpCall, bidRequest); // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getBids()).isEmpty(); - assertThat(result.getFledgeAuctionConfigs()) - .containsOnly(FledgeAuctionConfig.builder() + final ExtIgi igi = ExtIgi.builder() + .igs(singletonList(ExtIgiIgs.builder() .impId("impId1") .config(mapper.createObjectNode().put("somevalue", 1)) - .build()); + .build())) + .build(); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getBids()).isEmpty(); + assertThat(result.getIgi()).containsExactly(igi); } @Test diff --git a/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java b/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java index 66a1888a2a4..ae22bb5a300 100644 --- a/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pubmatic/PubmaticBidderTest.java @@ -42,7 +42,8 @@ import org.prebid.server.proto.openrtb.ext.request.pubmatic.ExtImpPubmaticKeyVal; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidVideo; -import org.prebid.server.proto.openrtb.ext.response.FledgeAuctionConfig; +import org.prebid.server.proto.openrtb.ext.response.ExtIgi; +import org.prebid.server.proto.openrtb.ext.response.ExtIgiIgs; import org.prebid.server.util.HttpUtil; import java.math.BigDecimal; @@ -1273,11 +1274,11 @@ public void makeBidderResponseShouldReturnBannerBidIfExtBidContainsIllegalBidTyp public void makeBidderResponseShouldReturnFledgeAuctionConfig() throws JsonProcessingException { // given final BidResponse bidResponse = givenBidResponse(bidBuilder -> bidBuilder.impid("imp_id")); - final ObjectNode fledgeAuctionConfig = mapper.createObjectNode(); + final ObjectNode auctionConfig = mapper.createObjectNode(); final PubmaticBidResponse bidResponseWithFledge = PubmaticBidResponse.builder() .cur(bidResponse.getCur()) .seatbid(bidResponse.getSeatbid()) - .ext(PubmaticExtBidResponse.of(Map.of("imp_id", fledgeAuctionConfig))) + .ext(PubmaticExtBidResponse.of(Map.of("imp_id", auctionConfig))) .build(); final BidderCall httpCall = givenHttpCall(mapper.writeValueAsString(bidResponseWithFledge)); @@ -1289,11 +1290,11 @@ public void makeBidderResponseShouldReturnFledgeAuctionConfig() throws JsonProce assertThat(result.getBids()) .containsExactly(BidderBid.of(Bid.builder().impid("imp_id").build(), banner, "USD")); - final FledgeAuctionConfig expectedFledge = FledgeAuctionConfig.builder() - .impId("imp_id") - .config(fledgeAuctionConfig) + final ExtIgi igi = ExtIgi.builder() + .igs(singletonList(ExtIgiIgs.builder().impId("imp_id").config(auctionConfig).build())) .build(); - assertThat(result.getFledgeAuctionConfigs()).containsExactly(expectedFledge); + + assertThat(result.getIgi()).containsExactly(igi); } @Test diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-response.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-response.json index 1ecdc0136a1..c2c8e90c3f5 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-auction-ix-response.json @@ -28,47 +28,7 @@ "ix": "{{ ix.response_time_ms }}" }, "prebid": { - "auctiontimestamp": 0, - "fledge": { - "auctionconfigs": [ - { - "impid": "imp_id", - "bidder": "ix", - "adapter": "ix", - "config": { - "seller": "https://test.casalemedia.com", - "decisionLogicUrl": "https://test.casalemedia.com/decision-logic.js", - "trustedScoringSignalsURL": "https://test.casalemedia.com/123", - "interestGroupBuyers": [ - "https://test.com" - ], - "sellerSignals": { - "callbackURL": "https://test.casalemedia.com/callback/1", - "debugURL": "https://test.casalemedia.com/debug/1", - "width": 300, - "height": 250 - }, - "sellerTimeout": 150, - "perBuyerSignals": { - "https://test.com": [ - { - "key": "value" - } - ] - }, - "perBuyerCurrencies": { - "*": "USD" - }, - "sellerCurrency": "USD", - "requestedSize": { - "width": 300, - "height": 250 - }, - "maxTrustedBiddingSignalsURLLength": 1000 - } - } - ] - } + "auctiontimestamp": 0 }, "tmaxrequest": 5000 } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request.json index ef303b14b8e..5478c38a82b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-request.json @@ -16,6 +16,11 @@ }, "ext": { "ae": 1, + "igs": [ + { + "ae": 1 + } + ], "tid": "${json-unit.any-string}", "bidder": { "siteId": "10002" diff --git a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-response.json index c62e7c626b9..9d9d7035ed7 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/ix/test-ix-bid-response.json @@ -13,43 +13,5 @@ ], "seat": "seatId6" } - ], - "ext": { - "protectedAudienceAuctionConfigs": [ - { - "bidId": "imp_id", - "config": { - "seller": "https://test.casalemedia.com", - "decisionLogicUrl": "https://test.casalemedia.com/decision-logic.js", - "trustedScoringSignalsURL": "https://test.casalemedia.com/123", - "interestGroupBuyers": [ - "https://test.com" - ], - "sellerSignals": { - "callbackURL": "https://test.casalemedia.com/callback/1", - "debugURL": "https://test.casalemedia.com/debug/1", - "width": 300, - "height": 250 - }, - "sellerTimeout": 150, - "perBuyerSignals": { - "https://test.com": [ - { - "key": "value" - } - ] - }, - "perBuyerCurrencies": { - "*": "USD" - }, - "sellerCurrency": "USD", - "requestedSize": { - "width": 300, - "height": 250 - }, - "maxTrustedBiddingSignalsURLLength": 1000 - } - } - ] - } + ] }