11package org .prebid .server .bidder .sspbc ;
22
3- import com .fasterxml .jackson .core .type .TypeReference ;
4- import com .fasterxml .jackson .databind .JsonNode ;
5- import com .fasterxml .jackson .databind .node .ObjectNode ;
6- import com .iab .openrtb .request .Banner ;
73import com .iab .openrtb .request .BidRequest ;
8- import com .iab .openrtb .request .Format ;
9- import com .iab .openrtb .request .Imp ;
10- import com .iab .openrtb .request .Site ;
114import com .iab .openrtb .response .Bid ;
125import com .iab .openrtb .response .BidResponse ;
136import com .iab .openrtb .response .SeatBid ;
7+ import io .vertx .core .http .HttpMethod ;
148import org .apache .commons .collections4 .CollectionUtils ;
15- import org .apache .commons .lang3 .StringUtils ;
169import org .apache .http .client .utils .URIBuilder ;
1710import org .prebid .server .bidder .Bidder ;
1811import org .prebid .server .bidder .model .BidderBid ;
1912import org .prebid .server .bidder .model .BidderCall ;
2013import org .prebid .server .bidder .model .BidderError ;
2114import org .prebid .server .bidder .model .HttpRequest ;
2215import org .prebid .server .bidder .model .Result ;
23- import org .prebid .server .bidder .sspbc .request .SspbcRequestType ;
2416import org .prebid .server .exception .PreBidException ;
2517import org .prebid .server .json .DecodeException ;
2618import org .prebid .server .json .JacksonMapper ;
27- import org .prebid .server .proto .openrtb .ext .ExtPrebid ;
28- import org .prebid .server .proto .openrtb .ext .request .sspbc .ExtImpSspbc ;
2919import org .prebid .server .proto .openrtb .ext .response .BidType ;
3020import org .prebid .server .util .BidderUtil ;
3121import org .prebid .server .util .HttpUtil ;
32- import org .prebid .server .util .ObjectUtil ;
3322
3423import java .net .URISyntaxException ;
35- import java .util .ArrayList ;
24+ import java .util .Collection ;
3625import java .util .Collections ;
37- import java .util .Comparator ;
3826import java .util .List ;
39- import java .util .Map ;
4027import java .util .Objects ;
41- import java .util .Optional ;
42- import java .util .function .Function ;
43- import java .util .function .UnaryOperator ;
44- import java .util .stream .Collectors ;
4528
46- public class SspbcBidder implements Bidder <BidRequest > {
29+ public class SspbcBidder implements Bidder <SspbcRequest > {
4730
48- private static final TypeReference <ExtPrebid <?, ExtImpSspbc >> SSPBC_EXT_TYPE_REFERENCE =
49- new TypeReference <>() {
50- };
51- private static final String ADAPTER_VERSION = "5.8" ;
52- private static final String IMP_FALLBACK_SIZE = "1x1" ;
53- private static final String PREBID_SERVER_INTEGRATION_TYPE = "4" ;
54- private static final String BANNER_TEMPLATE = """
55- <html><head><title></title><meta charset="UTF-8"><meta name="viewport"\
56- content="width=device-width, initial-scale=1.0"><style> body { background-color:\
57- transparent; margin: 0; padding: 0; }</style><script> window.rekid = {{.SiteId}};\
58- window.slot = {{.SlotId}}; window.adlabel = '{{.AdLabel}}'; window.pubid = '{{.PubId}}';\
59- window.wp_sn = 'sspbc_go'; window.page = '{{.Page}}'; window.ref = '{{.Referer}}';\
60- window.mcad = {{.McAd}}; window.inver = '{{.Inver}}'; </script></head><body><div id="c"></div><script\
61- async crossorigin nomodule src="//std.wpcdn.pl/wpjslib/wpjslib-inline.js"\
62- id="wpjslib"></script><script async crossorigin type="module"\
63- src="//std.wpcdn.pl/wpjslib6/wpjslib-inline.js" id="wpjslib6"></script></body></html>""" ;
31+ private static final String ADAPTER_VERSION = "6.0" ;
6432
6533 private final String endpointUrl ;
6634 private final JacksonMapper mapper ;
@@ -71,247 +39,66 @@ public SspbcBidder(String endpointUrl, JacksonMapper mapper) {
7139 }
7240
7341 @ Override
74- public Result <List <HttpRequest <BidRequest >>> makeHttpRequests (BidRequest request ) {
75- if (request .getSite () == null ) {
76- return Result .withError (BidderError .badInput ("BidRequest.site not provided" ));
77- }
78-
79- final Map <Imp , ExtImpSspbc > impToExt ;
80-
81- try {
82- impToExt = createImpToExt (request );
83- } catch (PreBidException e ) {
84- return Result .withError (BidderError .badInput (e .getMessage ()));
85- }
86-
87- final SspbcRequestType requestType = getRequestType (impToExt );
88- final List <Imp > imps = new ArrayList <>();
89- String siteId = "" ;
90-
91- for (Imp imp : request .getImp ()) {
92- final ExtImpSspbc extImpSspbc = impToExt .get (imp );
93- final String extImpSspbcSiteId = extImpSspbc .getSiteId ();
94- if (StringUtils .isNotEmpty (extImpSspbcSiteId )) {
95- siteId = extImpSspbcSiteId ;
96- }
97-
98- imps .add (updateImp (imp , requestType , extImpSspbc ));
99- }
100-
101- try {
102- return Result .withValue (createRequest (updateBidRequest (request , imps , requestType , siteId )));
103- } catch (PreBidException e ) {
104- return Result .withError (BidderError .badInput (e .getMessage ()));
105- }
106- }
107-
108- private Map <Imp , ExtImpSspbc > createImpToExt (BidRequest request ) {
109- return request .getImp ()
110- .stream ()
111- .collect (Collectors .toMap (Function .identity (), this ::parseImpExt ));
112- }
113-
114- private ExtImpSspbc parseImpExt (Imp imp ) {
115- try {
116- return mapper .mapper ().convertValue (imp .getExt (), SSPBC_EXT_TYPE_REFERENCE ).getBidder ();
117- } catch (IllegalArgumentException e ) {
118- throw new PreBidException (e .getMessage ());
119- }
120- }
121-
122- private Imp updateImp (Imp imp , SspbcRequestType requestType , ExtImpSspbc extImpSspbc ) {
123- final String originalImpId = imp .getId ();
124-
125- return imp .toBuilder ()
126- .id (resolveImpId (originalImpId , extImpSspbc .getId (), requestType ))
127- .tagid (originalImpId )
128- .ext (createImpExt (imp ))
42+ public Result <List <HttpRequest <SspbcRequest >>> makeHttpRequests (BidRequest request ) {
43+ return Result .withValue (createHttpRequest (request ));
44+ }
45+
46+ private HttpRequest <SspbcRequest > createHttpRequest (BidRequest request ) {
47+ final SspbcRequest outgoingRequest = SspbcRequest .of (request );
48+ return HttpRequest .<SspbcRequest >builder ()
49+ .method (HttpMethod .POST )
50+ .uri (makeUrl (endpointUrl ))
51+ .headers (HttpUtil .headers ())
52+ .impIds (BidderUtil .impIds (outgoingRequest .getBidRequest ()))
53+ .body (mapper .encodeToBytes (outgoingRequest ))
54+ .payload (outgoingRequest )
12955 .build ();
13056 }
13157
132- private String resolveImpId (String originalImpId , String extImpId , SspbcRequestType requestType ) {
133- return StringUtils .isNotEmpty (extImpId ) && requestType != SspbcRequestType .REQUEST_TYPE_ONE_CODE
134- ? extImpId
135- : originalImpId ;
136- }
137-
138- private ObjectNode createImpExt (Imp imp ) {
139- return mapper .mapper ().createObjectNode ()
140- .set ("data" , mapper .mapper ().createObjectNode ()
141- .put ("pbslot" , imp .getTagid ())
142- .put ("pbsize" , getImpSize (imp )));
143- }
144-
145- private BidRequest updateBidRequest (BidRequest bidRequest ,
146- List <Imp > imps ,
147- SspbcRequestType requestType ,
148- String siteId ) {
149-
150- return bidRequest .toBuilder ()
151- .imp (imps )
152- .site (updateSite (bidRequest .getSite (), requestType , siteId ))
153- .test (updateTest (requestType , bidRequest .getTest ()))
154- .build ();
155- }
156-
157- private Site updateSite (Site site , SspbcRequestType requestType , String siteId ) {
158- return site .toBuilder ()
159- .id (resolveSiteId (requestType , siteId ))
160- .domain (getUri (site .getPage ()).getHost ())
161- .build ();
162- }
163-
164- private static String resolveSiteId (SspbcRequestType requestType , String siteId ) {
165- return requestType == SspbcRequestType .REQUEST_TYPE_ONE_CODE || StringUtils .isBlank (siteId )
166- ? StringUtils .EMPTY
167- : siteId ;
168- }
169-
170- private static Integer updateTest (SspbcRequestType requestType , Integer test ) {
171- return requestType == SspbcRequestType .REQUEST_TYPE_TEST ? 1 : test ;
172- }
173-
174- private String getImpSize (Imp imp ) {
175- final List <Format > formats = ObjectUtil .getIfNotNull (imp .getBanner (), Banner ::getFormat );
176- if (CollectionUtils .isEmpty (formats )) {
177- return IMP_FALLBACK_SIZE ;
178- }
179-
180- return formats .stream ()
181- .max (Comparator .comparing (SspbcBidder ::formatToArea ))
182- .filter (format -> formatToArea (format ) > 0 )
183- .map (format -> String .format ("%dx%d" , format .getW (), format .getH ()))
184- .orElse (IMP_FALLBACK_SIZE );
185- }
186-
187- private static int formatToArea (Format format ) {
188- final Integer w = ObjectUtil .getIfNotNull (format , Format ::getW );
189- final Integer h = ObjectUtil .getIfNotNull (format , Format ::getH );
190-
191- return w != null && h != null ? w * h : 0 ;
192- }
193-
194- private SspbcRequestType getRequestType (Map <Imp , ExtImpSspbc > impToExt ) {
195- for (ExtImpSspbc extImpSspbc : impToExt .values ()) {
196- if (extImpSspbc .getTest () != 0 ) {
197- return SspbcRequestType .REQUEST_TYPE_TEST ;
198- }
199-
200- if (StringUtils .isAnyEmpty (extImpSspbc .getSiteId (), extImpSspbc .getId ())) {
201- return SspbcRequestType .REQUEST_TYPE_ONE_CODE ;
202- }
203- }
204-
205- return SspbcRequestType .REQUEST_TYPE_STANDARD ;
206- }
207-
208- private HttpRequest <BidRequest > createRequest (BidRequest request ) {
209- return BidderUtil .defaultRequest (request , updateUrl (getUri (endpointUrl )), mapper );
210- }
211-
212- private static URIBuilder getUri (String endpointUrl ) {
213- final URIBuilder uri ;
58+ private static String makeUrl (String endpointUrl ) {
21459 try {
215- uri = new URIBuilder (endpointUrl );
60+ return new URIBuilder (endpointUrl )
61+ .addParameter ("bdver" , ADAPTER_VERSION )
62+ .build ()
63+ .toString ();
21664 } catch (URISyntaxException e ) {
21765 throw new PreBidException ("Malformed URL: %s." .formatted (endpointUrl ));
21866 }
219- return uri ;
220- }
221-
222- private String updateUrl (URIBuilder uriBuilder ) {
223- return uriBuilder
224- .addParameter ("bdver" , ADAPTER_VERSION )
225- .addParameter ("inver" , PREBID_SERVER_INTEGRATION_TYPE )
226- .toString ();
22767 }
22868
22969 @ Override
230- public Result <List <BidderBid >> makeBids (BidderCall <BidRequest > httpCall , BidRequest bidRequest ) {
70+ public Result <List <BidderBid >> makeBids (BidderCall <SspbcRequest > httpCall , BidRequest bidRequest ) {
23171 try {
23272 final BidResponse bidResponse = mapper .decodeValue (httpCall .getResponse ().getBody (), BidResponse .class );
233- return Result .withValues (extractBids (bidResponse , httpCall . getRequest (). getPayload () ));
73+ return Result .withValues (extractBids (bidResponse ));
23474 } catch (PreBidException | DecodeException e ) {
23575 return Result .withError (BidderError .badServerResponse (e .getMessage ()));
23676 }
23777 }
23878
239- private List <BidderBid > extractBids (BidResponse bidResponse , BidRequest bidRequest ) {
79+ private List <BidderBid > extractBids (BidResponse bidResponse ) {
24080 if (bidResponse == null || CollectionUtils .isEmpty (bidResponse .getSeatbid ())) {
24181 return Collections .emptyList ();
24282 }
24383
24484 return bidResponse .getSeatbid ().stream ()
24585 .filter (Objects ::nonNull )
246- .map (seatBid -> CollectionUtils . emptyIfNull ( seatBid . getBid () )
247- . stream ( )
248- . filter ( Objects :: nonNull )
249- . map ( bid -> toBidderBid ( bid , seatBid . getSeat (), bidResponse . getCur (), bidRequest )) )
250- .flatMap ( UnaryOperator . identity ( ))
86+ .map (SeatBid :: getBid )
87+ . filter ( Objects :: nonNull )
88+ . flatMap ( Collection :: stream )
89+ . filter ( Objects :: nonNull )
90+ .map ( bid -> BidderBid . of ( bid , getBidType ( bid ), bidResponse . getCur () ))
25191 .toList ();
25292 }
25393
254- private BidderBid toBidderBid (Bid bid , String seat , String currency , BidRequest bidRequest ) {
255- if (StringUtils .isEmpty (bid .getAdm ())) {
256- throw new PreBidException ("Bid format is not supported" );
257- }
258-
259- final ObjectNode bidExt = bid .getExt ();
260- final Bid updatedBid = bid .toBuilder ()
261- .impid (getImpTagId (bidRequest .getImp (), bid ))
262- .adm (createBannerAd (
263- bid ,
264- stringOrNull (bidExt , "adlabel" ),
265- stringOrNull (bidExt , "pubid" ),
266- stringOrNull (bidExt , "siteid" ),
267- stringOrNull (bidExt , "slotid" ),
268- seat ,
269- bidRequest ))
270- .build ();
271-
272- return BidderBid .of (updatedBid , BidType .banner , currency );
273- }
274-
275- private static String stringOrNull (ObjectNode bidExt , String property ) {
276- return Optional .ofNullable (bidExt )
277- .map (ext -> ext .get (property ))
278- .map (JsonNode ::asText )
279- .orElse (StringUtils .EMPTY );
280- }
281-
282- private static String getImpTagId (List <Imp > imps , Bid bid ) {
283- return imps .stream ()
284- .filter (imp -> Objects .equals (imp .getId (), bid .getImpid ()))
285- .map (Imp ::getTagid )
286- .findAny ()
287- .orElseThrow (() -> new PreBidException ("imp not found" ));
288- }
289-
290- private String createBannerAd (Bid bid ,
291- String adlabel ,
292- String pubid ,
293- String siteid ,
294- String slotid ,
295- String seat ,
296- BidRequest bidRequest ) {
297- if (bid .getAdm ().contains ("<!--preformatted-->" )) {
298- return bid .getAdm ();
299- }
300-
301- final ObjectNode mcad = mapper .mapper ().createObjectNode ()
302- .put ("id" , bidRequest .getId ())
303- .put ("seat" , seat )
304- .set ("seatbid" , mapper .mapper ()
305- .convertValue (SeatBid .builder ().bid (Collections .singletonList (bid )).build (), JsonNode .class ));
306-
307- return BANNER_TEMPLATE
308- .replace ("{{.SiteId}}" , siteid )
309- .replace ("{{.SlotId}}" , slotid )
310- .replace ("{{.AdLabel}}" , adlabel )
311- .replace ("{{.PubId}}" , pubid )
312- .replace ("{{.Page}}" , bidRequest .getSite ().getPage ())
313- .replace ("{{.Referer}}" , bidRequest .getSite ().getRef ())
314- .replace ("{{.McAd}}" , mcad .toString ())
315- .replace ("{{.Inver}}" , PREBID_SERVER_INTEGRATION_TYPE );
94+ private static BidType getBidType (Bid bid ) {
95+ return switch (bid .getMtype ()) {
96+ case 1 -> BidType .banner ;
97+ case 2 -> BidType .video ;
98+ case 3 -> BidType .audio ;
99+ case 4 -> BidType .xNative ;
100+ case null -> throw new PreBidException ("Bid mtype is required" );
101+ default -> throw new PreBidException ("unsupported MType: %s." .formatted (bid .getMtype ()));
102+ };
316103 }
317104}
0 commit comments