-
Notifications
You must be signed in to change notification settings - Fork 187
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
Yieldlab: Add Digital Service Act (DSA) support #3011
Yieldlab: Add Digital Service Act (DSA) support #3011
Conversation
f381f45
to
edf9d9f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me
expectedDsa.put("adrender", 2); | ||
expectedDsa.set("transparency", transparencies); | ||
|
||
final var actualDsa = result.getValue().get(0).getBid().getExt().get("dsa"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style of repository requires strict types instead of vars
); | ||
} | ||
|
||
@ParameterizedTest(name = "{index} - {0}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not using @ParameterizedTest
inside repository
Separate test cases == separate tests
import java.util.List; | ||
|
||
@AllArgsConstructor(staticName = "of") | ||
@Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@value(staticConstructor = "of")
String behalf; | ||
String paid; | ||
Integer adrender; | ||
List<Transparency> transparency; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We prefer to have empty line after each field
if (dsa == null) { | ||
return null; | ||
} | ||
final var ext = mapper.mapper().createObjectNode(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please replace each var with strict type
} | ||
|
||
private static String encodeTransparency(ExtRegsDsaTransparency transparency) { | ||
return "%s~%s".formatted(transparency.getDomain(), encodeTransparencyParams(transparency.getDsaParams())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick. %s~%s
can be class level constant,
_
on line 296 also
} | ||
|
||
private static boolean hasDsa(BidRequest request) { | ||
return request.getRegs() != null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you can use Optional approach
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand what you mean by "Optional approach".
dsaRequestParams.put("dsadatatopub", dsa.getDataToPub().toString()); | ||
} | ||
|
||
if (dsa.getTransparency() != null && !dsa.getTransparency().isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dsa.getTransparency()
Can be extracted to separate variable
.map(Regs::getExt) | ||
.map(ExtRegs::getDsa) | ||
.map(YieldlabBidder::extractDsaRequestParamsFromDsaRegsExtension) | ||
.orElse(Map.of()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better use the lazy version: .orElseGet(Map::of)
.collect(Collectors.joining(TRANSPARENCY_TEMPLATE_DELIMITER)); | ||
} | ||
|
||
private static Predicate<ExtRegsDsaTransparency> transparencyIsValid() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should rather be the predicate itself, e.g. accept the transparency object as a parameter and return a boolean. It should be referenced as YieldlabBidder::transparencyIsValid
. I would also call the method isTransparencyValid
.
|
||
private static Predicate<ExtRegsDsaTransparency> transparencyIsValid() { | ||
return transparency -> | ||
!Objects.isNull(transparency.getDomain()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transparency.getDomain() != null
is more expressive IMHO.
dsaRequestParams.put("dsatransparency", encodeTransparenciesAsString(dsaTransparency)); | ||
} | ||
|
||
return dsaRequestParams.entrySet().stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? All values that might go into the map are checked for nullness already.
List<Transparency> transparency; | ||
|
||
@AllArgsConstructor(staticName = "of") | ||
@Value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is staticConstructor
missing here as well?
07acc54
to
35f54c5
Compare
src/main/java/org/prebid/server/bidder/yieldlab/YieldlabBidder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/prebid/server/bidder/yieldlab/YieldlabBidder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/prebid/server/bidder/yieldlab/YieldlabBidder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/prebid/server/bidder/yieldlab/YieldlabBidder.java
Outdated
Show resolved
Hide resolved
src/main/java/org/prebid/server/bidder/yieldlab/YieldlabBidder.java
Outdated
Show resolved
Hide resolved
try { | ||
dsaNode = mapper.mapper().convertValue(dsa, JsonNode.class); | ||
} catch (IllegalArgumentException e) { | ||
throw new PreBidException("Failed to serialize DSA object", e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like the PBS Go version has a more detailed error message fmt.Errorf("failed to make JSON for seatbid.bid.ext for adslotID %v. This is most likely a programming issue", bid.ID)
Perhaps it's worth keeping it here as well
Also, according to the PBS Go version the error shouldn't disrupt other bids processing, so the error has to be reported and the ext has to be null in this case. Please double-check
src/test/java/org/prebid/server/bidder/yieldlab/YieldlabBidderTest.java
Outdated
Show resolved
Hide resolved
src/test/java/org/prebid/server/bidder/yieldlab/YieldlabBidderTest.java
Outdated
Show resolved
Hide resolved
|
||
private static boolean isTransparencyValid(ExtRegsDsaTransparency transparency) { | ||
return transparency.getDomain() != null | ||
&& StringUtils.isNotBlank(transparency.getDomain()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can use just isNotBlank
, it covers not null case as well
@@ -486,7 +492,8 @@ private ObjectNode resolveExtParameter(YieldlabDigitalServicesActResponse dsa) { | |||
try { | |||
dsaNode = mapper.mapper().convertValue(dsa, JsonNode.class); | |||
} catch (IllegalArgumentException e) { | |||
throw new PreBidException("Failed to serialize DSA object", e); | |||
logger.error("Failed to serialize DSA object for adslot {}", yieldlabResponse.getId(), e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this error should be returned in the response, not just logged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would require a big refactoring of the existing implementation as there is no support for returning valid bids along with failed bids. Yieldlab did not write the existing implementation, and we feel it is beyond the scope of this ticket to be refactoring the existing implementation. Our goal is simply to add DSA support since this is a legal requirement. We are fine with not reporting failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the comments not related to the scope, so feel free to ignore them, but please at least provide answers on comments related to difference in logic between PBS-Java and PBS-Go.
.map(Regs::getExt) | ||
.map(ExtRegs::getDsa) | ||
.map(YieldlabBidder::extractDsaRequestParamsFromDsaRegsExtension) | ||
.orElseGet(Map::of); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are preferring to use Collections.empty*
instead of *.of()
.
Also, as Collections.emptyMap()
(and Map.of()
) returns already created object, it's better to use .orElse
instead of orElseGet
.
So, please, change it with:
.orElse(Collections.emptyMap());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparing the logic with the GO version, I don't see any sorting operations for adSlots in GO. Does PBS-Java need this?
adSlotIds.stream().sorted().collect(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't needed, no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, i don't see anything similar in GO for ts
parameter:
isDebugEnabled(request) ? "200000" : ...
Does PBS-Java need to pass this debug value? Just out of interest I removed the debug part and all tests passed:
final String timestamp = String.valueOf(clock.instant().getEpochSecond());
Same question about
final int weekNumber = isDebugEnabled(bidRequest) ? 35 : ...
and
final String timestamp = isDebugEnabled(bidRequest)
? "200000"
: ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as we are concerned, it is not needed no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String.join("ylid:", user.getBuyeruid())
doesn't corresponds to GO logic. It just doesn't do anything.
It's need to be "ylid" + user.getBuyeruid()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a courtesy I can fix this, but this wasn't written by us and is also out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementing this breaks the tests constructExtImpShouldWorkWithDuplicateKeysTargeting
and makeHttpRequestsShouldSendRequestToModifiedUrlWithHeaders
, so I will have to decline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uriBuilder.addParameter("lat", resolveNumberParameter(geo.getLat()));
uriBuilder.addParameter("lon", resolveNumberParameter(geo.getLon()));
This code doesn't handle null
s as GO does.
Java: null
-> null
GO: nil
-> "0"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is out of scope, and I don't intend to change it.
} | ||
|
||
final List<ExtRegsDsaTransparency> dsaTransparency = dsa.getTransparency(); | ||
if (dsaTransparency != null && !dsaTransparency.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Predicate can be simplified: CollectionUtils.isNotEmpty(dsaTransparency)
|
||
final List<ExtRegsDsaTransparency> dsaTransparency = dsa.getTransparency(); | ||
if (dsaTransparency != null && !dsaTransparency.isEmpty()) { | ||
dsaRequestParams.put("dsatransparency", encodeTransparenciesAsString(dsaTransparency)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
encodeTransparencyAsString
can return an empty string if all entries are invalid. PBS-GO doesn't add the dsatransparency
parameter in this case.
private static String encodeTransparenciesAsString(List<ExtRegsDsaTransparency> transparencies) { | ||
return transparencies.stream() | ||
.filter(YieldlabBidder::isTransparencyValid) | ||
.map(YieldlabBidder::encodeTransparency) | ||
.collect(Collectors.joining(TRANSPARENCY_TEMPLATE_DELIMITER)); | ||
} | ||
|
||
private static boolean isTransparencyValid(ExtRegsDsaTransparency transparency) { | ||
return StringUtils.isNotBlank(transparency.getDomain()) | ||
&& transparency.getDsaParams() != null | ||
&& CollectionUtils.isNotEmpty(transparency.getDsaParams()); | ||
} | ||
|
||
private static String encodeTransparency(ExtRegsDsaTransparency transparency) { | ||
return TRANSPARENCY_TEMPLATE.formatted(transparency.getDomain(), | ||
encodeTransparencyParams(transparency.getDsaParams())); | ||
} | ||
|
||
private static String encodeTransparencyParams(List<Integer> dsaParams) { | ||
return dsaParams.stream().map(Objects::toString).collect(Collectors.joining( | ||
TRANSPARENCY_TEMPLATE_PARAMS_DELIMITER)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some differences with GO logic:
- transparency0 - invalid, transparency1 - valid
Java:"transparency1"
GO:"~~transparency1"
- transparency0 - valid, transparency1 - invalid, transparency2 - valid
Java:"transparency0~~transparency2"
GO:"transparency0~~~~transparency2"
Similar problems with ~
separator:
- If
transparency1.dsaParams
is empty
Java:"transparency0~~transparency2"
GO:"transparency0~~domain1~~~transparency2"
- ...
And so on :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me the Java implementation looks correct, and the GO version incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of scope, but....
Can i ask you to extract collectImpExt(imps)
from ExtImpYieldlab getMatchedExtImp(...)
method, so it will be out of for loop
on bids? Just to make it from O(nk)
to O(n)
.... 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry, but I must decline. Like you said it is out of scope.
final ObjectNode ext = mapper.mapper().createObjectNode(); | ||
final JsonNode dsaNode; | ||
try { | ||
dsaNode = mapper.mapper().convertValue(dsa, JsonNode.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mapper.mapper().valueToTree(dsa)
This ports this Go PR
A review and approval from a member of the Yieldlab team are required. (@brushmate, @rey1128, @nkloeber)