22
33import com .fasterxml .jackson .core .JsonProcessingException ;
44import com .fasterxml .jackson .core .type .TypeReference ;
5+ import com .fasterxml .jackson .databind .JsonNode ;
6+ import com .fasterxml .jackson .databind .node .ObjectNode ;
57import com .iab .openrtb .request .App ;
68import com .iab .openrtb .request .BidRequest ;
79import com .iab .openrtb .request .Device ;
1416import io .netty .handler .codec .http .HttpHeaderValues ;
1517import io .vertx .core .MultiMap ;
1618import io .vertx .core .http .HttpMethod ;
19+ import io .vertx .core .logging .Logger ;
20+ import io .vertx .core .logging .LoggerFactory ;
21+ import org .apache .commons .collections4 .CollectionUtils ;
1722import org .apache .commons .lang3 .ObjectUtils ;
1823import org .apache .commons .lang3 .StringUtils ;
1924import org .apache .http .client .utils .URIBuilder ;
2328import org .prebid .server .bidder .model .BidderError ;
2429import org .prebid .server .bidder .model .HttpRequest ;
2530import org .prebid .server .bidder .model .Result ;
31+ import org .prebid .server .bidder .yieldlab .model .YieldlabDigitalServicesActResponse ;
2632import org .prebid .server .bidder .yieldlab .model .YieldlabResponse ;
2733import org .prebid .server .exception .PreBidException ;
2834import org .prebid .server .json .DecodeException ;
2935import org .prebid .server .json .JacksonMapper ;
3036import org .prebid .server .proto .openrtb .ext .ExtPrebid ;
37+ import org .prebid .server .proto .openrtb .ext .request .ExtRegs ;
38+ import org .prebid .server .proto .openrtb .ext .request .ExtRegsDsa ;
39+ import org .prebid .server .proto .openrtb .ext .request .ExtRegsDsaTransparency ;
3140import org .prebid .server .proto .openrtb .ext .request .ExtRequest ;
3241import org .prebid .server .proto .openrtb .ext .request .ExtRequestPrebid ;
3342import org .prebid .server .proto .openrtb .ext .request .ExtUser ;
4150import java .util .ArrayList ;
4251import java .util .Calendar ;
4352import java .util .Collections ;
53+ import java .util .HashMap ;
4454import java .util .List ;
4555import java .util .Map ;
4656import java .util .Objects ;
57+ import java .util .Optional ;
4758import java .util .stream .Collectors ;
4859
4960public class YieldlabBidder implements Bidder <Void > {
5061
62+ private static final Logger logger = LoggerFactory .getLogger (YieldlabBidder .class );
5163 private static final TypeReference <ExtPrebid <?, ExtImpYieldlab >> YIELDLAB_EXT_TYPE_REFERENCE =
5264 new TypeReference <>() {
5365 };
@@ -58,6 +70,9 @@ public class YieldlabBidder implements Bidder<Void> {
5870 private static final String CREATIVE_ID = "%s%s%s" ;
5971 private static final String AD_SOURCE_BANNER = "<script src=\" %s\" ></script>" ;
6072 private static final String AD_SOURCE_URL = "https://ad.yieldlab.net/d/%s/%s/%s?%s" ;
73+ private static final String TRANSPARENCY_TEMPLATE = "%s~%s" ;
74+ private static final String TRANSPARENCY_TEMPLATE_PARAMS_DELIMITER = "_" ;
75+ private static final String TRANSPARENCY_TEMPLATE_DELIMITER = "~~" ;
6176 private static final String VAST_MARKUP = """
6277 <VAST version="2.0"><Ad id="%s"><Wrapper>
6378 <AdSystem>Yieldlab</AdSystem>
@@ -189,6 +204,8 @@ private String makeUrl(ExtImpYieldlab extImpYieldlab, BidRequest request) {
189204 uriBuilder .addParameter ("consent" , consent );
190205 }
191206
207+ extractDsaRequestParamsFromBidRequest (request ).forEach (uriBuilder ::addParameter );
208+
192209 return uriBuilder .toString ();
193210 }
194211
@@ -231,6 +248,63 @@ private static String getConsentParameter(User user) {
231248 return ObjectUtils .defaultIfNull (consent , "" );
232249 }
233250
251+ private static Map <String , String > extractDsaRequestParamsFromBidRequest (BidRequest request ) {
252+ return Optional .ofNullable (request .getRegs ())
253+ .map (Regs ::getExt )
254+ .map (ExtRegs ::getDsa )
255+ .map (YieldlabBidder ::extractDsaRequestParamsFromDsaRegsExtension )
256+ .orElse (Collections .emptyMap ());
257+ }
258+
259+ private static Map <String , String > extractDsaRequestParamsFromDsaRegsExtension (final ExtRegsDsa dsa ) {
260+ final Map <String , String > dsaRequestParams = new HashMap <>();
261+
262+ if (dsa .getDsaRequired () != null ) {
263+ dsaRequestParams .put ("dsarequired" , dsa .getDsaRequired ().toString ());
264+ }
265+
266+ if (dsa .getPubRender () != null ) {
267+ dsaRequestParams .put ("dsapubrender" , dsa .getPubRender ().toString ());
268+ }
269+
270+ if (dsa .getDataToPub () != null ) {
271+ dsaRequestParams .put ("dsadatatopub" , dsa .getDataToPub ().toString ());
272+ }
273+
274+ final List <ExtRegsDsaTransparency > dsaTransparency = dsa .getTransparency ();
275+ if (CollectionUtils .isNotEmpty (dsaTransparency )) {
276+ final String encodedTransparencies = encodeTransparenciesAsString (dsaTransparency );
277+ if (StringUtils .isNotBlank (encodedTransparencies )) {
278+ dsaRequestParams .put ("dsatransparency" , encodedTransparencies );
279+ }
280+ }
281+
282+ return dsaRequestParams ;
283+ }
284+
285+ private static String encodeTransparenciesAsString (List <ExtRegsDsaTransparency > transparencies ) {
286+ return transparencies .stream ()
287+ .filter (YieldlabBidder ::isTransparencyValid )
288+ .map (YieldlabBidder ::encodeTransparency )
289+ .collect (Collectors .joining (TRANSPARENCY_TEMPLATE_DELIMITER ));
290+ }
291+
292+ private static boolean isTransparencyValid (ExtRegsDsaTransparency transparency ) {
293+ return StringUtils .isNotBlank (transparency .getDomain ())
294+ && transparency .getDsaParams () != null
295+ && CollectionUtils .isNotEmpty (transparency .getDsaParams ());
296+ }
297+
298+ private static String encodeTransparency (ExtRegsDsaTransparency transparency ) {
299+ return TRANSPARENCY_TEMPLATE .formatted (transparency .getDomain (),
300+ encodeTransparencyParams (transparency .getDsaParams ()));
301+ }
302+
303+ private static String encodeTransparencyParams (List <Integer > dsaParams ) {
304+ return dsaParams .stream ().map (Objects ::toString ).collect (Collectors .joining (
305+ TRANSPARENCY_TEMPLATE_PARAMS_DELIMITER ));
306+ }
307+
234308 private static MultiMap resolveHeaders (Site site , Device device , User user ) {
235309 final MultiMap headers = MultiMap .caseInsensitiveMultiMap ()
236310 .add (HttpUtil .ACCEPT_HEADER , HttpHeaderValues .APPLICATION_JSON );
@@ -339,7 +413,8 @@ private Bid.BidBuilder addBidParams(YieldlabResponse yieldlabResponse, BidReques
339413 .dealid (resolveNumberParameter (yieldlabResponse .getPid ()))
340414 .crid (makeCreativeId (bidRequest , yieldlabResponse , matchedExtImp ))
341415 .w (resolveSizeParameter (yieldlabResponse .getAdSize (), true ))
342- .h (resolveSizeParameter (yieldlabResponse .getAdSize (), false ));
416+ .h (resolveSizeParameter (yieldlabResponse .getAdSize (), false ))
417+ .ext (resolveExtParameter (yieldlabResponse ));
343418
344419 return updatedBid ;
345420 }
@@ -408,4 +483,21 @@ private String makeNurl(BidRequest bidRequest, ExtImpYieldlab extImpYieldlab, Yi
408483 yieldlabResponse .getAdSize (),
409484 uriBuilder .toString ().replace ("?" , "" ));
410485 }
486+
487+ private ObjectNode resolveExtParameter (YieldlabResponse yieldlabResponse ) {
488+ final YieldlabDigitalServicesActResponse dsa = yieldlabResponse .getDsa ();
489+ if (dsa == null ) {
490+ return null ;
491+ }
492+ final ObjectNode ext = mapper .mapper ().createObjectNode ();
493+ final JsonNode dsaNode ;
494+ try {
495+ dsaNode = mapper .mapper ().valueToTree (dsa );
496+ } catch (IllegalArgumentException e ) {
497+ logger .error ("Failed to serialize DSA object for adslot {}" , yieldlabResponse .getId (), e );
498+ return null ;
499+ }
500+ ext .set ("dsa" , dsaNode );
501+ return ext ;
502+ }
411503}
0 commit comments