diff --git a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java index 9e533a2754d..af1da417fd2 100644 --- a/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java +++ b/src/main/java/org/prebid/server/auction/AuctionRequestFactory.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookieService; import org.prebid.server.exception.BlacklistedAccountException; import org.prebid.server.exception.BlacklistedAppException; @@ -62,8 +63,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.Currency; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -97,6 +101,7 @@ public class AuctionRequestFactory { private final ImplicitParametersExtractor paramsExtractor; private final IpAddressHelper ipAddressHelper; private final UidsCookieService uidsCookieService; + private final BidderCatalog bidderCatalog; private final RequestValidator requestValidator; private final InterstitialProcessor interstitialProcessor; private final TimeoutResolver timeoutResolver; @@ -117,6 +122,7 @@ public AuctionRequestFactory(long maxRequestSize, ImplicitParametersExtractor paramsExtractor, IpAddressHelper ipAddressHelper, UidsCookieService uidsCookieService, + BidderCatalog bidderCatalog, RequestValidator requestValidator, InterstitialProcessor interstitialProcessor, OrtbTypesResolver ortbTypesResolver, @@ -137,6 +143,7 @@ public AuctionRequestFactory(long maxRequestSize, this.paramsExtractor = Objects.requireNonNull(paramsExtractor); this.ipAddressHelper = Objects.requireNonNull(ipAddressHelper); this.uidsCookieService = Objects.requireNonNull(uidsCookieService); + this.bidderCatalog = Objects.requireNonNull(bidderCatalog); this.requestValidator = Objects.requireNonNull(requestValidator); this.interstitialProcessor = Objects.requireNonNull(interstitialProcessor); this.ortbTypesResolver = Objects.requireNonNull(ortbTypesResolver); @@ -645,15 +652,18 @@ private ExtRequest populateRequestExt(ExtRequest ext, BidRequest bidRequest, Lis final ExtRequestPrebid prebid = ext.getPrebid(); final ExtRequestTargeting updatedTargeting = targetingOrNull(prebid, getImpMediaTypes(imps)); + final Map updatedAliases = aliasesOrNull(prebid, imps); final ExtRequestPrebidCache updatedCache = cacheOrNull(prebid); final ExtRequestPrebidChannel updatedChannel = channelOrNull(prebid, bidRequest); - if (updatedTargeting != null || updatedCache != null || updatedChannel != null) { + if (updatedTargeting != null || updatedAliases != null || updatedCache != null || updatedChannel != null) { final ExtRequestPrebid.ExtRequestPrebidBuilder prebidBuilder = prebid != null ? prebid.toBuilder() : ExtRequestPrebid.builder(); return ExtRequest.of(prebidBuilder + .aliases(ObjectUtils.defaultIfNull(updatedAliases, + getIfNotNull(prebid, ExtRequestPrebid::getAliases))) .targeting(ObjectUtils.defaultIfNull(updatedTargeting, getIfNotNull(prebid, ExtRequestPrebid::getTargeting))) .cache(ObjectUtils.defaultIfNull(updatedCache, @@ -789,6 +799,49 @@ private static Set checkExistingMediaTypes(ExtMediaTypePriceGranularity return priceGranularityTypes; } + /** + * Returns aliases according to request.imp[i].ext.{bidder} + * or null (if no aliases at all or they are already presented in request). + */ + private Map aliasesOrNull(ExtRequestPrebid prebid, List imps) { + final Map aliases = getIfNotNullOrDefault(prebid, ExtRequestPrebid::getAliases, + Collections.emptyMap()); + + final Map preconfiguredAliases = bidderCatalog.names().stream() + .filter(bidder -> aliasOf(bidder) != null) + .collect(Collectors.toMap(Function.identity(), this::aliasOf)); + + // go through imps' bidders and figure out preconfigured aliases existing in bid request + final Map resolvedAliases = imps.stream() + .filter(Objects::nonNull) + .map(Imp::getExt) + .filter(Objects::nonNull) // request validator is not called yet + .flatMap(extImp -> StreamUtil.asStream(biddersFromImp(extImp))) + .filter(bidder -> !aliases.containsKey(bidder)) + .filter(preconfiguredAliases::containsKey) + .distinct() + .collect(Collectors.toMap(Function.identity(), preconfiguredAliases::get)); + + final Map result; + if (resolvedAliases.isEmpty()) { + result = null; + } else { + result = new HashMap<>(aliases); + result.putAll(resolvedAliases); + } + return result; + } + + private String aliasOf(String bidder) { + return bidderCatalog.bidderInfoByName(bidder).getAliasOf(); + } + + private static Iterator biddersFromImp(ObjectNode extImp) { + final JsonNode extPrebid = extImp.get(PREBID_EXT); + final JsonNode extPrebidBidder = isObjectNode(extPrebid) ? extPrebid.get(BIDDER_EXT) : null; + return isObjectNode(extPrebidBidder) ? extPrebidBidder.fieldNames() : Collections.emptyIterator(); + } + /** * Returns populated {@link ExtRequestPrebidCache} or null if no changes were applied. */ @@ -856,6 +909,10 @@ private static R getIfNotNull(T target, Function getter) { return target != null ? getter.apply(target) : null; } + private static R getIfNotNullOrDefault(T target, Function getter, R defaultValue) { + return ObjectUtils.defaultIfNull(getIfNotNull(target, getter), defaultValue); + } + /** * Performs thorough validation of fully constructed {@link BidRequest} that is going to be used to hold an auction. */ 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 67bdb2f1318..ce674c47070 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -189,6 +189,7 @@ AuctionRequestFactory auctionRequestFactory( ImplicitParametersExtractor implicitParametersExtractor, IpAddressHelper ipAddressHelper, UidsCookieService uidsCookieService, + BidderCatalog bidderCatalog, RequestValidator requestValidator, OrtbTypesResolver ortbTypesResolver, TimeoutResolver timeoutResolver, @@ -212,6 +213,7 @@ AuctionRequestFactory auctionRequestFactory( implicitParametersExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, new InterstitialProcessor(), ortbTypesResolver, diff --git a/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java b/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java index a7bc169425b..9d18461371a 100644 --- a/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java +++ b/src/test/java/org/prebid/server/auction/AuctionRequestFactoryTest.java @@ -30,6 +30,7 @@ import org.prebid.server.VertxTest; import org.prebid.server.auction.model.AuctionContext; import org.prebid.server.auction.model.IpAddress; +import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.cookie.UidsCookie; import org.prebid.server.cookie.UidsCookieService; import org.prebid.server.cookie.model.UidWithExpiry; @@ -65,6 +66,7 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions; import org.prebid.server.proto.openrtb.ext.request.ExtRequestTargeting; import org.prebid.server.proto.openrtb.ext.request.ExtSite; +import org.prebid.server.proto.response.BidderInfo; import org.prebid.server.settings.ApplicationSettings; import org.prebid.server.settings.model.Account; import org.prebid.server.settings.model.AccountStatus; @@ -75,7 +77,9 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.HashSet; import java.util.List; +import java.util.Map; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -113,6 +117,8 @@ public class AuctionRequestFactoryTest extends VertxTest { @Mock private UidsCookieService uidsCookieService; @Mock + private BidderCatalog bidderCatalog; + @Mock private RequestValidator requestValidator; @Mock private InterstitialProcessor interstitialProcessor; @@ -162,6 +168,7 @@ public void setUp() { paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -202,6 +209,7 @@ public void shouldReturnFailedFutureIfAccountIsEnforcedAndIdIsNotProvided() { paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -240,6 +248,7 @@ public void shouldReturnFailedFutureIfAccountIsEnforcedAndFailedGetAccountById() paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -313,6 +322,7 @@ public void shouldReturnFailedFutureIfRequestBodyExceedsMaxRequestSize() { paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1671,6 +1681,7 @@ public void shouldSetDefaultIncludeBidderKeysToFalseIfIncludeBidderKeysIsMissedA paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1713,6 +1724,7 @@ public void shouldSetCacheWinningonlyFromConfigWhenExtRequestPrebidIsNull() { paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1754,6 +1766,7 @@ public void shouldSetCacheWinningonlyFromConfigWhenExtRequestPrebidCacheIsNull() paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1795,6 +1808,7 @@ public void shouldSetCacheWinningonlyFromConfigWhenCacheWinningonlyIsNull() { paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1887,6 +1901,7 @@ public void shouldSetCacheWinningonlyFromRequestWhenCacheWinningonlyIsPresent() paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1930,6 +1945,7 @@ public void shouldNotSetCacheWinningonlyFromConfigWhenCacheWinningonlyIsNullAndC paramsExtractor, ipAddressHelper, uidsCookieService, + bidderCatalog, requestValidator, interstitialProcessor, ortbTypesResolver, @@ -1956,6 +1972,43 @@ public void shouldNotSetCacheWinningonlyFromConfigWhenCacheWinningonlyIsNullAndC assertThat(request.getExt()).isSameAs(extBidRequest); } + @Test + public void shouldAddPreconfiguredAliases() { + // given + final Imp imp1 = Imp.builder() + .ext(mapper.createObjectNode().set("requestScopedBidderAlias", mapper.createObjectNode())) + .build(); + final Imp imp2 = Imp.builder() + .ext(mapper.createObjectNode().set("configScopedBidderAlias", mapper.createObjectNode())) + .build(); + + givenBidRequest(BidRequest.builder() + .imp(asList(imp1, imp2)) + .ext(ExtRequest.of(ExtRequestPrebid.builder() + .aliases(singletonMap("requestScopedBidderAlias", "bidder1")) + .build())) + .build()); + + given(bidderCatalog.names()).willReturn(new HashSet<>(asList("bidder2", "configScopedBidderAlias"))); + given(bidderCatalog.bidderInfoByName("bidder2")) + .willReturn(BidderInfo.of(true, null, null, null, null, null, false, false)); + given(bidderCatalog.bidderInfoByName("configScopedBidderAlias")) + .willReturn(BidderInfo.of(true, "bidder2", null, null, null, null, false, false)); + + // when + final BidRequest request = factory.fromRequest(routingContext, 0L).result().getBidRequest(); + + // then + assertThat(singletonList(request)) + .extracting(BidRequest::getExt) + .extracting(ExtRequest::getPrebid) + .flatExtracting(extRequestPrebid -> extRequestPrebid.getAliases().entrySet()) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsOnly( + tuple("requestScopedBidderAlias", "bidder1"), + tuple("configScopedBidderAlias", "bidder2")); + } + @Test public void shouldSetRequestPrebidChannelWhenMissingInRequestAndSite() { // given @@ -2096,7 +2149,7 @@ public void shouldReturnFailedFutureIfRequestValidationFailed() { } @Test - public void shouldReturnFailedFutureIfEidsPermissionsContainsWrongDataType() throws JsonProcessingException { + public void shouldReturnFailedFutureIfEidsPermissionsContainsWrongDataType() { // given final BidRequest bidRequest = BidRequest.builder() .ext(ExtRequest.of(ExtRequestPrebid.builder() @@ -2125,7 +2178,7 @@ public void shouldReturnFailedFutureIfEidsPermissionsContainsWrongDataType() thr } @Test - public void shouldReturnFailedFutureIfEidsPermissionsBiddersContainsWrongDataType() throws JsonProcessingException { + public void shouldReturnFailedFutureIfEidsPermissionsBiddersContainsWrongDataType() { // given final BidRequest bidRequest = BidRequest.builder() .ext(ExtRequest.of(ExtRequestPrebid.builder()