Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add one more way for Rubicon bidder to represent User.BuyerUid. #1701

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions src/main/java/org/prebid/server/bidder/rubicon/RubiconBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public class RubiconBidder implements Bidder<BidRequest> {
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";
Expand All @@ -144,6 +145,7 @@ public class RubiconBidder implements Bidder<BidRequest> {
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";
Expand Down Expand Up @@ -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);
Expand All @@ -194,12 +196,11 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest bidRequ

final List<Imp> 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<Imp, ExtPrebid<ExtImpPrebid, ExtImpRubicon>> impToImpExt =
parseRubiconImpExts(imps, errors);

final Map<Imp, ExtPrebid<ExtImpPrebid, ExtImpRubicon>> impToImpExt = parseRubiconImpExts(imps, errors);
final String impLanguage = firstImpExtLanguage(impToImpExt.values());
final String uri = makeUri(bidRequest);

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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<ExtUserEid> extUserEids = extUser != null ? extUser.getEids() : null;
final String userBuyeruid = user != null ? user.getBuyeruid() : null;
final String resolvedBuyeruid = userBuyeruid != null ? userBuyeruid : resolveBuyeruidFromEids(extUserEids);
final Map<String, List<ExtUserEid>> sourceToUserEidExt = extUser != null
? specialExtUserEids(extUserEids)
: null;
Expand All @@ -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;
}

Expand All @@ -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)
Expand Down Expand Up @@ -1041,6 +1049,28 @@ private static ExtUserEidUid cleanExtUserEidUidStype(ExtUserEidUid extUserEidUid
ExtUserEidUidExt.of(extUserEidUidExt.getRtiPartner(), null));
}

private static String resolveBuyeruidFromEids(List<ExtUserEid> 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<String, List<ExtUserEid>> specialExtUserEids(List<ExtUserEid> eids) {
if (CollectionUtils.isEmpty(eids)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<List<HttpRequest<BidRequest>>> 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<List<HttpRequest<BidRequest>>> 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<List<HttpRequest<BidRequest>>> 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
Expand Down