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 bfe2bf29713..91ce7bc0f0e 100644 --- a/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java +++ b/src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java @@ -134,6 +134,11 @@ public class RubiconBidder implements Bidder { private static final String FPD_DFP_AD_UNIT_CODE_FIELD = "dfp_ad_unit_code"; private static final String FPD_KEYWORDS_FIELD = "keywords"; + private static final String PPUID_STYPE = "ppuid"; + private static final String SHA256EMAIL_STYPE = "sha256email"; + private static final String DMP_STYPE = "dmp"; + private static final Set STYPE_TO_REMOVE = new HashSet<>(Arrays.asList(PPUID_STYPE, SHA256EMAIL_STYPE, + DMP_STYPE)); private static final TypeReference> RUBICON_EXT_TYPE_REFERENCE = new TypeReference>() { }; @@ -677,18 +682,26 @@ private static List mapToRubiconSizeIds(List sizes) { } private User makeUser(User user, ExtImpRubicon rubiconImpExt) { + final String userId = user != null ? user.getId() : null; 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 Map> sourceToUserEidExt = extUser != null - ? specialExtUserEids(extUser.getEids()) + ? specialExtUserEids(extUserEids) : null; final List userExtTpIds = sourceToUserEidExt != null ? extractExtUserTpIds(sourceToUserEidExt) : null; + final boolean hasStypeToRemove = hasStypeToRemove(extUserEids); + final List resolvedExtUserEids = hasStypeToRemove + ? prepareExtUserEids(extUserEids) + : extUserEids; final RubiconUserExtRp userExtRp = rubiconUserExtRp(user, rubiconImpExt, sourceToUserEidExt); final ObjectNode userExtData = extUser != null ? extUser.getData() : null; final String liverampId = extractLiverampId(sourceToUserEidExt); - if (userExtRp == null && userExtTpIds == null && userExtData == null && liverampId == null) { + if (userExtRp == null && userExtTpIds == null && userExtData == null && liverampId == null + && resolvedId == null && !hasStypeToRemove) { return user; } @@ -696,7 +709,7 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { ? ExtUser.builder() .consent(extUser.getConsent()) .digitrust(extUser.getDigitrust()) - .eids(extUser.getEids()) + .eids(resolvedExtUserEids) .build() : ExtUser.builder().build(); @@ -709,6 +722,7 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { final User.UserBuilder userBuilder = user != null ? user.toBuilder() : User.builder(); return userBuilder + .id(resolvedId) .gender(null) .yob(null) .geo(null) @@ -716,6 +730,65 @@ private User makeUser(User user, ExtImpRubicon rubiconImpExt) { .build(); } + private String resolveUserId(ExtUser extUser) { + final List extUserEids = extUser != null ? extUser.getEids() : null; + return CollectionUtils.emptyIfNull(extUserEids) + .stream() + .map(extUserEid -> getIdFromFirstUuidWithStypePpuid(extUserEid.getUids())) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + private String getIdFromFirstUuidWithStypePpuid(List extUserEidUids) { + return CollectionUtils.emptyIfNull(extUserEidUids).stream() + .filter(Objects::nonNull) + .filter(extUserEidUid -> Objects.equals(PPUID_STYPE, getUserEidUidStype(extUserEidUid))) + .map(ExtUserEidUid::getId) + .findFirst() + .orElse(null); + } + + private String getUserEidUidStype(ExtUserEidUid extUserEidUid) { + final ExtUserEidUidExt extUserEidUidExt = extUserEidUid.getExt(); + return extUserEidUidExt != null ? extUserEidUidExt.getStype() : null; + } + + private boolean hasStypeToRemove(List extUserEids) { + return CollectionUtils.emptyIfNull(extUserEids).stream() + .filter(Objects::nonNull) + .map(ExtUserEid::getUids) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(ExtUserEidUid::getExt) + .filter(Objects::nonNull) + .map(ExtUserEidUidExt::getStype) + .anyMatch(STYPE_TO_REMOVE::contains); + } + + private List prepareExtUserEids(List extUserEids) { + return CollectionUtils.emptyIfNull(extUserEids).stream() + .filter(Objects::nonNull) + .map(RubiconBidder::prepareExtUserEid) + .collect(Collectors.toList()); + } + + private static ExtUserEid prepareExtUserEid(ExtUserEid extUserEid) { + final List extUserEidUids = CollectionUtils.emptyIfNull(extUserEid.getUids()).stream() + .filter(Objects::nonNull) + .map(RubiconBidder::cleanExtUserEidUidStype) + .collect(Collectors.toList()); + return ExtUserEid.of(extUserEid.getSource(), extUserEid.getId(), extUserEidUids, extUserEid.getExt()); + } + + private static ExtUserEidUid cleanExtUserEidUidStype(ExtUserEidUid extUserEidUid) { + final ExtUserEidUidExt extUserEidUidExt = extUserEidUid.getExt(); + return STYPE_TO_REMOVE.contains(extUserEidUidExt.getStype()) + ? ExtUserEidUid.of(extUserEidUid.getId(), extUserEidUid.getAtype(), + ExtUserEidUidExt.of(extUserEidUidExt.getRtiPartner(), null)) + : extUserEidUid; + } + private static Map> specialExtUserEids(List eids) { if (CollectionUtils.isEmpty(eids)) { return null; diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserEidUidExt.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserEidUidExt.java index 858e9af3e91..a1bfe62ba53 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserEidUidExt.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ExtUserEidUidExt.java @@ -13,4 +13,6 @@ public class ExtUserEidUidExt { @JsonProperty("rtiPartner") String rtiPartner; + + String stype; } 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 cb199d60acf..1140a6ce84e 100644 --- a/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/rubicon/RubiconBidderTest.java @@ -754,8 +754,8 @@ public void makeHttpRequestsShouldCreateUserExtTpIdWithAdServerEidSource() { .ext(ExtUser.builder() .eids(singletonList(ExtUserEid.of("adserver.org", null, singletonList( - ExtUserEidUid.of("adServerUid", null, ExtUserEidUidExt.of("TDID"))), - null))) + ExtUserEidUid.of("adServerUid", null, + ExtUserEidUidExt.of("TDID", null))), null))) .build()) .build()), builder -> builder.video(Video.builder().build()), identity()); @@ -774,7 +774,7 @@ public void makeHttpRequestsShouldCreateUserExtTpIdWithAdServerEidSource() { "adserver.org", null, singletonList(ExtUserEidUid.of("adServerUid", null, - ExtUserEidUidExt.of("TDID"))), + ExtUserEidUidExt.of("TDID", null))), null))) .build(), RubiconUserExt.builder() @@ -782,6 +782,91 @@ public void makeHttpRequestsShouldCreateUserExtTpIdWithAdServerEidSource() { .build())); } + @Test + public void makeHttpRequestsShouldCreateUserIdIfMissingFromFirstUidStypePpuid() { + // given + final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of(null, null, + asList( + ExtUserEidUid.of("id1", null, ExtUserEidUidExt.of(null, "other")), + ExtUserEidUid.of("id2", null, ExtUserEidUidExt.of(null, "ppuid")), + ExtUserEidUid.of("id3", 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.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).doesNotContainNull() + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .extracting(request -> request.getUser().getId()) + .containsOnly("id2"); + } + + @Test + public void makeHttpRequestsShouldNotCreateUseIdIfMissingWhenNoUidWithPpuidType() { + // given + final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of(null, null, + asList( + ExtUserEidUid.of("id1", null, ExtUserEidUidExt.of(null, "other")), + ExtUserEidUid.of("id2", 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.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).doesNotContainNull() + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .extracting(request -> request.getUser().getId()).element(0).isNull(); + } + + @Test + public void makeHttpRequestsShouldRemoveStypesPpuidSha256emailDmp() { + // given + final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() + .ext(ExtUser.builder() + .eids(singletonList(ExtUserEid.of("source", "id", + asList( + ExtUserEidUid.of("id1", null, ExtUserEidUidExt.of(null, "other")), + ExtUserEidUid.of("id2", null, ExtUserEidUidExt.of(null, "ppuid")), + ExtUserEidUid.of("id3", null, ExtUserEidUidExt.of(null, "sha256email")), + ExtUserEidUid.of("id4", null, ExtUserEidUidExt.of(null, "dmp"))), + null))) + .build()) + .build()), + builder -> builder.video(Video.builder().build()), identity()); + + // when + final Result>> result = rubiconBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).doesNotContainNull() + .extracting(httpRequest -> mapper.readValue(httpRequest.getBody(), BidRequest.class)) + .extracting(request -> request.getUser().getExt()).hasSize(1).element(0) + .isEqualTo(ExtUser.builder() + .eids(singletonList(ExtUserEid.of("source", "id", + asList( + ExtUserEidUid.of("id1", null, ExtUserEidUidExt.of(null, "other")), + ExtUserEidUid.of("id2", null, ExtUserEidUidExt.of(null, null)), + ExtUserEidUid.of("id3", null, ExtUserEidUidExt.of(null, null)), + ExtUserEidUid.of("id4", null, ExtUserEidUidExt.of(null, null))), + null))) + .build()); + } + @Test public void makeHttpRequestsShouldCreateUserExtTpIdForFirstLiveintentAndAdserver() { // given @@ -792,9 +877,9 @@ public void makeHttpRequestsShouldCreateUserExtTpIdForFirstLiveintentAndAdserver final ExtUserEid liveintentUid2 = ExtUserEid.of("liveintent.com", null, singletonList(ExtUserEidUid.of("liveintentUid2", null, null)), null); final ExtUserEid adserverUid = ExtUserEid.of("adserver.org", null, - singletonList(ExtUserEidUid.of("adServerUid", null, ExtUserEidUidExt.of("TDID"))), null); + singletonList(ExtUserEidUid.of("adServerUid", null, ExtUserEidUidExt.of("TDID", null))), null); final ExtUserEid notSpecialSource = ExtUserEid.of("notSpecialSource", null, - singletonList(ExtUserEidUid.of("notSpecialSource", null, ExtUserEidUidExt.of("TDID"))), null); + singletonList(ExtUserEidUid.of("notSpecialSource", null, ExtUserEidUidExt.of("TDID", null))), null); final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() .ext(ExtUser.builder() .eids(Arrays.asList(liveintentUid1, liveintentUid2, adserverUid, notSpecialSource)) @@ -897,7 +982,8 @@ public void makeHttpRequestsShouldNotCreateUserExtTpIdWithAdServerEidSourceIfExt final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() .ext(ExtUser.builder() .eids(singletonList(ExtUserEid.of("adserver.org", null, - singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of(null))), null))) + singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of(null, null))), + null))) .build()) .build()), builder -> builder.video(Video.builder().build()), identity()); @@ -912,7 +998,7 @@ public void makeHttpRequestsShouldNotCreateUserExtTpIdWithAdServerEidSourceIfExt .extracting(request -> request.getUser().getExt()) .containsOnly(ExtUser.builder() .eids(singletonList(ExtUserEid.of("adserver.org", null, - singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of(null))), null))) + singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of(null, null))), null))) .build()); } @@ -978,7 +1064,8 @@ public void makeHttpRequestsShouldNotCreateUserExtTpIdWithUnknownEidSource() { final BidRequest bidRequest = givenBidRequest(builder -> builder.user(User.builder() .ext(ExtUser.builder() .eids(singletonList(ExtUserEid.of("unknownSource", null, - singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of("eidUidId"))), + singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of("eidUidId", + null))), null))) .build()) .build()), @@ -994,7 +1081,8 @@ public void makeHttpRequestsShouldNotCreateUserExtTpIdWithUnknownEidSource() { .extracting(request -> request.getUser().getExt()) .containsOnly(ExtUser.builder() .eids(singletonList(ExtUserEid.of("unknownSource", null, - singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of("eidUidId"))), null))) + singletonList(ExtUserEidUid.of("id", null, ExtUserEidUidExt.of("eidUidId", null))), + null))) .build()); }