diff --git a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java index 33ecec313ad..0baf340de5c 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -127,6 +127,7 @@ public class RubiconBidder implements Bidder { private static final String ADSERVER_EID = "adserver.org"; private static final String LIVEINTENT_EID = "liveintent.com"; private static final String LIVERAMP_EID = "liveramp.com"; + private static final String SOURCE_RUBICON = "rubiconproject.com"; private static final String FPD_GPID_FIELD = "gpid"; private static final String FPD_SECTIONCAT_FIELD = "sectioncat"; @@ -144,6 +145,7 @@ public class RubiconBidder implements Bidder { private static final String PREBID_EXT = "prebid"; private static final String PPUID_STYPE = "ppuid"; + private static final String OTHER_STYPE = "other"; private static final String SHA256EMAIL_STYPE = "sha256email"; private static final String DMP_STYPE = "dmp"; private static final String XAPI_CURRENCY = "USD"; @@ -179,7 +181,7 @@ public RubiconBidder(String endpoint, JacksonMapper mapper) { this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpoint)); - this.supportedVendors = new HashSet<>(supportedVendors); + this.supportedVendors = Set.copyOf(Objects.requireNonNull(supportedVendors)); this.generateBidId = generateBidId; this.currencyConversionService = Objects.requireNonNull(currencyConversionService); this.mapper = Objects.requireNonNull(mapper); @@ -194,12 +196,11 @@ public Result>> makeHttpRequests(BidRequest bidRequ final List imps = extractValidImps(bidRequest, errors); if (CollectionUtils.isEmpty(imps)) { - errors.add(BidderError.of("There are no valid impressions to create bid request to rubicon bidder", - BidderError.Type.bad_input)); - return Result.of(Collections.emptyList(), errors); + errors.add(BidderError.badInput("There are no valid impressions to create bid request to rubicon bidder")); + return Result.withErrors(errors); } - final Map> impToImpExt = - parseRubiconImpExts(imps, errors); + + final Map> impToImpExt = parseRubiconImpExts(imps, errors); final String impLanguage = firstImpExtLanguage(impToImpExt.values()); final String uri = makeUri(bidRequest); @@ -270,9 +271,8 @@ private static boolean isValidType(Imp imp) { private BidderError impTypeErrorMessage(Imp imp) { final BidType type = resolveExpectedBidType(imp); - return BidderError.of( - String.format("Impression with id %s rejected with invalid type `%s`." + " Allowed types are banner and" - + " video.", imp.getId(), type != null ? type.name() : "unknown"), BidderError.Type.bad_input); + return BidderError.badInput(String.format("Impression with id %s rejected with invalid type `%s`." + + " Allowed types are banner and video.", imp.getId(), type != null ? type.name() : "unknown")); } private static BidType resolveExpectedBidType(Imp imp) { @@ -938,6 +938,8 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { final ExtUser extUser = user != null ? user.getExt() : null; final String resolvedId = userId == null ? resolveUserId(extUser) : null; final List extUserEids = extUser != null ? extUser.getEids() : null; + final String userBuyeruid = user != null ? user.getBuyeruid() : null; + final String resolvedBuyeruid = userBuyeruid != null ? userBuyeruid : resolveBuyeruidFromEids(extUserEids); final Map> sourceToUserEidExt = extUser != null ? specialExtUserEids(extUserEids) : null; @@ -952,8 +954,13 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { final ObjectNode userExtData = extUser != null ? extUser.getData() : null; final String liverampId = extractLiverampId(sourceToUserEidExt); - if (userExtRp == null && userExtTpIds == null && userExtData == null && liverampId == null - && resolvedId == null && !hasStypeToRemove) { + if (userExtRp == null + && userExtTpIds == null + && userExtData == null + && liverampId == null + && resolvedId == null + && Objects.equals(userBuyeruid, resolvedBuyeruid) + && !hasStypeToRemove) { return user; } @@ -975,6 +982,7 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { return userBuilder .id(ObjectUtils.defaultIfNull(resolvedId, userId)) + .buyeruid(resolvedBuyeruid) .gender(null) .yob(null) .geo(null) @@ -1041,6 +1049,28 @@ private static ExtUserEidUid cleanExtUserEidUidStype(ExtUserEidUid extUserEidUid ExtUserEidUidExt.of(extUserEidUidExt.getRtiPartner(), null)); } + private static String resolveBuyeruidFromEids(List eids) { + return CollectionUtils.emptyIfNull(eids).stream() + .filter(Objects::nonNull) + .filter(eid -> SOURCE_RUBICON.equals(eid.getSource())) + .map(ExtUserEid::getUids) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .filter(RubiconBidder::validateExtUserEidUidForUserBuyeruid) + .map(ExtUserEidUid::getId) + .findFirst() + .orElse(null); + + } + + private static boolean validateExtUserEidUidForUserBuyeruid(ExtUserEidUid uid) { + final ExtUserEidUidExt uidExt = ObjectUtil.getIfNotNull(uid, ExtUserEidUid::getExt); + final String uidExtStype = ObjectUtil.getIfNotNull(uidExt, ExtUserEidUidExt::getStype); + + return StringUtils.equalsAny(uidExtStype, PPUID_STYPE, OTHER_STYPE); + } + private static Map> specialExtUserEids(List eids) { if (CollectionUtils.isEmpty(eids)) { return null; diff --git a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java index f106bb11376..ddc4d36d487 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -1173,6 +1173,100 @@ public void makeHttpRequestsShouldNotCreateUserIfVisitorAndConsentNotPresent() { .containsOnly((User) null); } + @Test + public void makeHttpRequestsShouldUseUserBuyeruidIfPresent() { + // given + final BidRequest bidRequest = givenBidRequest( + builder -> builder.user(User.builder() + .buyeruid("buyeruid") + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of( + "rubiconproject.com", + null, + singletonList(ExtUserEidUid.of( + "extUserEidUidId", + null, + ExtUserEidUidExt.of(null, "ppuid") + )), + null))) + .build()) + .build()), + builder -> builder.video(Video.builder().build()), identity()); + + // when + final Result>> result = rubiconBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getUser) + .extracting(User::getBuyeruid) + .containsExactly("buyeruid"); + assertThat(result.getErrors()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldUseUidIdIfUserBuyeruidAbsentAndSpecialEidSourceAndStypeIsPpuid() { + // given + final BidRequest bidRequest = givenBidRequest( + builder -> builder.user(User.builder() + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of( + "rubiconproject.com", + null, + singletonList(ExtUserEidUid.of( + "extUserEidUidId", + null, + ExtUserEidUidExt.of(null, "ppuid") + )), + null))) + .build()) + .build()), + builder -> builder.video(Video.builder().build()), identity()); + + // when + final Result>> result = rubiconBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getUser) + .extracting(User::getBuyeruid) + .containsExactly("extUserEidUidId"); + assertThat(result.getErrors()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldUseUidIdIfUserBuyeruidAbsentAndSpecialEidSourceAndStypeIsOther() { + // given + final BidRequest bidRequest = givenBidRequest( + builder -> builder.user(User.builder() + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of( + "rubiconproject.com", + null, + singletonList(ExtUserEidUid.of( + "extUserEidUidId", + null, + ExtUserEidUidExt.of(null, "other") + )), + null))) + .build()) + .build()), + builder -> builder.video(Video.builder().build()), identity()); + + // when + final Result>> result = rubiconBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getUser) + .extracting(User::getBuyeruid) + .containsExactly("extUserEidUidId"); + assertThat(result.getErrors()).isEmpty(); + } + @Test public void makeHttpRequestsShouldCreateUserExtTpIdWithAdServerEidSource() { // given