Skip to content

Commit 627586d

Browse files
New Blis Adapter (#4107)
1 parent cd8f6c7 commit 627586d

12 files changed

Lines changed: 653 additions & 0 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package org.prebid.server.bidder.blis;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.iab.openrtb.request.BidRequest;
5+
import com.iab.openrtb.request.Imp;
6+
import com.iab.openrtb.response.Bid;
7+
import com.iab.openrtb.response.BidResponse;
8+
import com.iab.openrtb.response.SeatBid;
9+
import io.vertx.core.MultiMap;
10+
import org.apache.commons.collections4.CollectionUtils;
11+
import org.apache.commons.lang3.StringUtils;
12+
import org.prebid.server.bidder.Bidder;
13+
import org.prebid.server.bidder.model.BidderBid;
14+
import org.prebid.server.bidder.model.BidderCall;
15+
import org.prebid.server.bidder.model.BidderError;
16+
import org.prebid.server.bidder.model.HttpRequest;
17+
import org.prebid.server.bidder.model.Result;
18+
import org.prebid.server.exception.PreBidException;
19+
import org.prebid.server.json.DecodeException;
20+
import org.prebid.server.json.JacksonMapper;
21+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
22+
import org.prebid.server.proto.openrtb.ext.request.blis.ExtImpBlis;
23+
import org.prebid.server.proto.openrtb.ext.response.BidType;
24+
import org.prebid.server.util.BidderUtil;
25+
import org.prebid.server.util.HttpUtil;
26+
27+
import java.math.BigDecimal;
28+
import java.util.ArrayList;
29+
import java.util.Collection;
30+
import java.util.Collections;
31+
import java.util.List;
32+
import java.util.Objects;
33+
import java.util.stream.Collectors;
34+
35+
public class BlisBidder implements Bidder<BidRequest> {
36+
37+
private static final TypeReference<ExtPrebid<?, ExtImpBlis>> BLIS_EXT_TYPE_REFERENCE = new TypeReference<>() {
38+
};
39+
private static final String AUCTION_PRICE_MACRO = "${AUCTION_PRICE}";
40+
private static final String SUPPLY_ID_MACRO = "{{SupplyId}}";
41+
42+
private final String endpointUrl;
43+
private final JacksonMapper mapper;
44+
45+
public BlisBidder(String endpointUrl, JacksonMapper mapper) {
46+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
47+
this.mapper = Objects.requireNonNull(mapper);
48+
}
49+
50+
@Override
51+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
52+
final String supplyId;
53+
try {
54+
supplyId = parseImpExt(request.getImp().getFirst()).getSupplyId();
55+
} catch (PreBidException e) {
56+
return Result.withError(BidderError.badInput(e.getMessage()));
57+
}
58+
59+
return Result.withValue(BidderUtil.defaultRequest(request, makeHeaders(supplyId), makeUrl(supplyId), mapper));
60+
}
61+
62+
private ExtImpBlis parseImpExt(Imp imp) {
63+
try {
64+
return mapper.mapper().convertValue(imp.getExt(), BLIS_EXT_TYPE_REFERENCE).getBidder();
65+
} catch (IllegalArgumentException e) {
66+
throw new PreBidException("Error parsing imp.ext: " + e.getMessage());
67+
}
68+
}
69+
70+
private static MultiMap makeHeaders(String supplyId) {
71+
return HttpUtil.headers().add("X-Supply-Partner-Id", supplyId);
72+
}
73+
74+
private String makeUrl(String supplyId) {
75+
return endpointUrl.replace(SUPPLY_ID_MACRO, HttpUtil.encodeUrl(supplyId));
76+
}
77+
78+
@Override
79+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
80+
try {
81+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
82+
final List<BidderError> errors = new ArrayList<>();
83+
return Result.of(extractBids(bidResponse, errors), errors);
84+
} catch (DecodeException e) {
85+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
86+
}
87+
}
88+
89+
private static List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
90+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
91+
return Collections.emptyList();
92+
}
93+
return bidsFromResponse(bidResponse, errors);
94+
}
95+
96+
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> errors) {
97+
return bidResponse.getSeatbid().stream()
98+
.filter(Objects::nonNull)
99+
.map(SeatBid::getBid)
100+
.filter(Objects::nonNull)
101+
.flatMap(Collection::stream)
102+
.filter(Objects::nonNull)
103+
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
104+
.filter(Objects::nonNull)
105+
.collect(Collectors.toList());
106+
}
107+
108+
private static BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
109+
final BidType bidType = getBidType(bid, errors);
110+
return bidType != null
111+
? BidderBid.of(resolveMacros(bid), bidType, currency)
112+
: null;
113+
}
114+
115+
private static Bid resolveMacros(Bid bid) {
116+
final BigDecimal price = bid.getPrice();
117+
final String priceAsString = price != null ? price.toPlainString() : "0";
118+
119+
return bid.toBuilder()
120+
.nurl(StringUtils.replace(bid.getNurl(), AUCTION_PRICE_MACRO, priceAsString))
121+
.adm(StringUtils.replace(bid.getAdm(), AUCTION_PRICE_MACRO, priceAsString))
122+
.burl(StringUtils.replace(bid.getBurl(), AUCTION_PRICE_MACRO, priceAsString))
123+
.build();
124+
}
125+
126+
private static BidType getBidType(Bid bid, List<BidderError> errors) {
127+
return switch (bid.getMtype()) {
128+
case 1 -> BidType.banner;
129+
case 2 -> BidType.video;
130+
case 4 -> BidType.xNative;
131+
case null, default -> {
132+
errors.add(BidderError.badServerResponse(
133+
"Failed to parse media type of impression ID " + bid.getImpid()));
134+
yield null;
135+
}
136+
};
137+
}
138+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.prebid.server.proto.openrtb.ext.request.blis;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpBlis {
8+
9+
@JsonProperty("spid")
10+
String supplyId;
11+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.prebid.server.spring.config.bidder;
2+
3+
import org.prebid.server.bidder.BidderDeps;
4+
import org.prebid.server.bidder.blis.BlisBidder;
5+
import org.prebid.server.json.JacksonMapper;
6+
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
7+
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
8+
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
9+
import org.prebid.server.spring.env.YamlPropertySourceFactory;
10+
import org.springframework.beans.factory.annotation.Value;
11+
import org.springframework.boot.context.properties.ConfigurationProperties;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.context.annotation.PropertySource;
15+
16+
import jakarta.validation.constraints.NotBlank;
17+
18+
@Configuration
19+
@PropertySource(value = "classpath:/bidder-config/blis.yaml", factory = YamlPropertySourceFactory.class)
20+
public class BlisBidderConfiguration {
21+
22+
private static final String BIDDER_NAME = "blis";
23+
24+
@Bean("blisConfigurationProperties")
25+
@ConfigurationProperties("adapters.blis")
26+
BidderConfigurationProperties configurationProperties() {
27+
return new BidderConfigurationProperties();
28+
}
29+
30+
@Bean
31+
BidderDeps blisBidderDeps(BidderConfigurationProperties blisConfigurationProperties,
32+
@NotBlank @Value("${external-url}") String externalUrl,
33+
JacksonMapper mapper) {
34+
35+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
36+
.withConfig(blisConfigurationProperties)
37+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38+
.bidderCreator(config -> new BlisBidder(config.getEndpoint(), mapper))
39+
.assemble();
40+
}
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
adapters:
2+
blis:
3+
endpoint: https://prebid.lb.infinity.blismedia.com/rtb/213/{{SupplyId}}
4+
modifying-vast-xml-allowed: true
5+
endpoint-compression: gzip
6+
ortb-version: "2.6"
7+
meta-info:
8+
maintainer-email: prebid-support@blis.com
9+
app-media-types:
10+
- banner
11+
- video
12+
- native
13+
site-media-types:
14+
- banner
15+
- video
16+
- native
17+
supported-vendors:
18+
vendor-id: 94
19+
usersync:
20+
cookie-family-name: blis
21+
redirect:
22+
url: https://tr.blismedia.com/v1/api/sync/prebid?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&r={{redirect_url}}
23+
support-cors: false
24+
uid-macro: '%%BLIS_USER_TOKEN%%'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Blis Adapter Params",
4+
"description": "A schema which validates params accepted by the Blis adapter",
5+
"type": "object",
6+
"properties": {
7+
"spid": {
8+
"type": "string",
9+
"minLength": 1,
10+
"description": "Unique supply partner ID provided by Blis"
11+
}
12+
},
13+
"required": [
14+
"spid"
15+
]
16+
}

0 commit comments

Comments
 (0)