Skip to content

Commit be14062

Browse files
AntoxaAntoxicRitesh Ghodrao
authored andcommitted
Missena Adapter: Add formats and settings params (prebid#3970)
1 parent aa7d545 commit be14062

10 files changed

Lines changed: 384 additions & 128 deletions

File tree

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
11
package org.prebid.server.bidder.missena;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.iab.openrtb.request.Eid;
5+
import com.iab.openrtb.request.SupplyChain;
36
import lombok.Builder;
47
import lombok.Value;
58

6-
@Builder(toBuilder = true)
9+
import java.math.BigDecimal;
10+
import java.util.List;
11+
712
@Value
13+
@Builder(toBuilder = true)
814
public class MissenaAdRequest {
915

10-
String requestId;
16+
@JsonProperty("adunit")
17+
String adUnit;
18+
19+
@JsonProperty("buyeruid")
20+
String buyerUid;
21+
22+
Integer coppa;
23+
24+
String currency;
25+
26+
@JsonProperty("userEids")
27+
List<Eid> userEids;
1128

12-
int timeout;
29+
BigDecimal floor;
30+
31+
String floorCurrency;
32+
33+
@JsonProperty("consent_required")
34+
Boolean gdpr;
35+
36+
@JsonProperty("consent_string")
37+
String gdprConsent;
38+
39+
@JsonProperty("ik")
40+
String idempotencyKey;
1341

1442
String referer;
1543

1644
String refererCanonical;
1745

18-
String consentString;
46+
String requestId;
47+
48+
SupplyChain schain;
49+
50+
Long timeout;
51+
52+
String url;
1953

20-
boolean consentRequired;
54+
MissenaUserParams params;
2155

22-
String placement;
56+
String usPrivacy;
2357

24-
String test;
58+
String version;
2559
}

src/main/java/org/prebid/server/bidder/missena/MissenaBidder.java

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.iab.openrtb.request.Imp;
77
import com.iab.openrtb.request.Regs;
88
import com.iab.openrtb.request.Site;
9+
import com.iab.openrtb.request.Source;
910
import com.iab.openrtb.request.User;
1011
import com.iab.openrtb.response.Bid;
1112
import io.vertx.core.MultiMap;
@@ -16,7 +17,9 @@
1617
import org.prebid.server.bidder.model.BidderCall;
1718
import org.prebid.server.bidder.model.BidderError;
1819
import org.prebid.server.bidder.model.HttpRequest;
20+
import org.prebid.server.bidder.model.Price;
1921
import org.prebid.server.bidder.model.Result;
22+
import org.prebid.server.currency.CurrencyConversionService;
2023
import org.prebid.server.exception.PreBidException;
2124
import org.prebid.server.json.DecodeException;
2225
import org.prebid.server.json.JacksonMapper;
@@ -25,8 +28,13 @@
2528
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
2629
import org.prebid.server.proto.openrtb.ext.request.missena.ExtImpMissena;
2730
import org.prebid.server.proto.openrtb.ext.response.BidType;
31+
import org.prebid.server.util.BidderUtil;
2832
import org.prebid.server.util.HttpUtil;
33+
import org.prebid.server.version.PrebidVersionProvider;
2934

35+
import java.math.BigDecimal;
36+
import java.net.MalformedURLException;
37+
import java.net.URL;
3038
import java.util.ArrayList;
3139
import java.util.Collections;
3240
import java.util.List;
@@ -37,31 +45,40 @@ public class MissenaBidder implements Bidder<MissenaAdRequest> {
3745

3846
private static final TypeReference<ExtPrebid<?, ExtImpMissena>> TYPE_REFERENCE = new TypeReference<>() {
3947
};
40-
private static final int AD_REQUEST_DEFAULT_TIMEOUT = 2000;
48+
private static final String USD_CURRENCY = "USD";
49+
private static final String EUR_CURRENCY = "EUR";
50+
private static final String PUBLISHER_ID_MACRO = "{{PublisherID}}";
4151

4252
private final String endpointUrl;
4353
private final JacksonMapper mapper;
54+
private final CurrencyConversionService currencyConversionService;
55+
private final PrebidVersionProvider prebidVersionProvider;
56+
57+
public MissenaBidder(String endpointUrl,
58+
JacksonMapper mapper,
59+
CurrencyConversionService currencyConversionService,
60+
PrebidVersionProvider prebidVersionProvider) {
4461

45-
public MissenaBidder(String endpointUrl, JacksonMapper mapper) {
4662
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
4763
this.mapper = Objects.requireNonNull(mapper);
64+
this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
65+
this.prebidVersionProvider = Objects.requireNonNull(prebidVersionProvider);
4866
}
4967

5068
@Override
5169
public Result<List<HttpRequest<MissenaAdRequest>>> makeHttpRequests(BidRequest request) {
52-
final List<HttpRequest<MissenaAdRequest>> requests = new ArrayList<>();
5370
final List<BidderError> errors = new ArrayList<>();
5471

5572
for (Imp imp : request.getImp()) {
5673
try {
5774
final ExtImpMissena extImp = parseImpExt(imp);
58-
requests.add(makeHttpRequest(request, imp.getId(), extImp));
75+
final HttpRequest<MissenaAdRequest> httpRequest = makeHttpRequest(request, imp, extImp);
76+
return Result.of(Collections.singletonList(httpRequest), errors);
5977
} catch (PreBidException e) {
6078
errors.add(BidderError.badInput(e.getMessage()));
6179
}
6280
}
63-
64-
return Result.of(requests, errors);
81+
return Result.withErrors(errors);
6582
}
6683

6784
private ExtImpMissena parseImpExt(Imp imp) {
@@ -72,30 +89,90 @@ private ExtImpMissena parseImpExt(Imp imp) {
7289
}
7390
}
7491

75-
private HttpRequest<MissenaAdRequest> makeHttpRequest(BidRequest request, String impId, ExtImpMissena extImp) {
92+
private HttpRequest<MissenaAdRequest> makeHttpRequest(BidRequest request, Imp imp, ExtImpMissena extImp) {
7693
final Site site = request.getSite();
94+
final User user = request.getUser();
95+
final Regs regs = request.getRegs();
96+
final Device device = request.getDevice();
97+
final Source source = request.getSource();
98+
99+
final String requestCurrency = resolveCurrency(request.getCur());
100+
final Price floorInfo = resolveBidFloor(imp, request, requestCurrency);
101+
102+
final MissenaUserParams userParams = MissenaUserParams.builder()
103+
.formats(extImp.getFormats())
104+
.placement(extImp.getPlacement())
105+
.testMode(extImp.getTestMode())
106+
.settings(extImp.getSettings())
107+
.build();
77108

78109
final MissenaAdRequest missenaAdRequest = MissenaAdRequest.builder()
110+
.adUnit(imp.getId())
111+
.buyerUid(user != null ? user.getBuyeruid() : null)
112+
.coppa(regs != null ? regs.getCoppa() : null)
113+
.currency(requestCurrency)
114+
.userEids(user != null ? user.getEids() : null)
115+
.floor(floorInfo.getValue())
116+
.floorCurrency(floorInfo.getCurrency())
117+
.gdpr(isGdpr(regs))
118+
.gdprConsent(getUserConsent(user))
119+
.idempotencyKey(request.getId())
120+
.referer(site != null ? site.getPage() : null)
121+
.refererCanonical(site != null ? site.getDomain() : null)
79122
.requestId(request.getId())
80-
.timeout(AD_REQUEST_DEFAULT_TIMEOUT)
81-
.referer(site == null ? null : site.getPage())
82-
.refererCanonical(site == null ? null : site.getDomain())
83-
.consentString(getUserConsent(request.getUser()))
84-
.consentRequired(isGdpr(request.getRegs()))
85-
.placement(extImp.getPlacement())
86-
.test(extImp.getTestMode())
123+
.schain(source != null ? source.getSchain() : null)
124+
.timeout(request.getTmax())
125+
.params(userParams)
126+
.version(prebidVersionProvider.getNameVersionRecord())
87127
.build();
88128

89129
return HttpRequest.<MissenaAdRequest>builder()
90130
.method(HttpMethod.POST)
91-
.uri(makeUrl(extImp.getApiKey()))
92-
.headers(makeHeaders(request.getDevice(), site))
93-
.impIds(Collections.singleton(impId))
131+
.uri(resolveEndpointUrl(extImp.getApiKey()))
132+
.headers(makeHeaders(device, site))
133+
.impIds(Collections.singleton(imp.getId()))
94134
.body(mapper.encodeToBytes(missenaAdRequest))
95135
.payload(missenaAdRequest)
96136
.build();
97137
}
98138

139+
private Price resolveBidFloor(Imp imp, BidRequest bidRequest, String targetCurrency) {
140+
final Price initialBidFloorPrice = Price.of(imp.getBidfloorcur(), imp.getBidfloor());
141+
return BidderUtil.isValidPrice(initialBidFloorPrice)
142+
? convertBidFloor(initialBidFloorPrice, imp.getId(), bidRequest, targetCurrency)
143+
: initialBidFloorPrice;
144+
}
145+
146+
private Price convertBidFloor(Price bidFloorPrice, String impId, BidRequest bidRequest, String targetCurrency) {
147+
final String bidFloorCur = bidFloorPrice.getCurrency();
148+
149+
try {
150+
final BigDecimal convertedPrice = currencyConversionService
151+
.convertCurrency(bidFloorPrice.getValue(), bidRequest, bidFloorCur, targetCurrency);
152+
153+
return Price.of(targetCurrency, convertedPrice);
154+
} catch (PreBidException e) {
155+
throw new PreBidException("Unable to convert provided bid floor currency from %s to %s for imp `%s`"
156+
.formatted(bidFloorCur, targetCurrency, impId));
157+
}
158+
}
159+
160+
private String resolveCurrency(List<String> requestCurrencies) {
161+
String currency = USD_CURRENCY;
162+
163+
for (String requestCurrency : requestCurrencies) {
164+
if (USD_CURRENCY.equalsIgnoreCase(requestCurrency)) {
165+
return USD_CURRENCY;
166+
}
167+
168+
if (EUR_CURRENCY.equalsIgnoreCase(requestCurrency)) {
169+
currency = EUR_CURRENCY;
170+
}
171+
}
172+
173+
return currency;
174+
}
175+
99176
private MultiMap makeHeaders(Device device, Site site) {
100177
final MultiMap headers = HttpUtil.headers();
101178

@@ -105,15 +182,21 @@ private MultiMap makeHeaders(Device device, Site site) {
105182
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, device.getIpv6());
106183
}
107184

108-
if (site != null) {
185+
if (site != null && StringUtils.isNotBlank(site.getPage())) {
109186
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, site.getPage());
187+
try {
188+
final URL url = new URL(site.getPage());
189+
final String origin = url.getProtocol() + "://" + url.getHost();
190+
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.ORIGIN_HEADER, origin);
191+
} catch (MalformedURLException e) {
192+
// do nothing
193+
}
110194
}
111-
112195
return headers;
113196
}
114197

115-
private String makeUrl(String apiKey) {
116-
return endpointUrl + "?t=%s".formatted(apiKey);
198+
private String resolveEndpointUrl(String apiKey) {
199+
return endpointUrl.replace(PUBLISHER_ID_MACRO, HttpUtil.encodeUrl(apiKey));
117200
}
118201

119202
private static boolean isGdpr(Regs regs) {
@@ -128,7 +211,8 @@ private static String getUserConsent(User user) {
128211
return Optional.ofNullable(user)
129212
.map(User::getExt)
130213
.map(ExtUser::getConsent)
131-
.orElse(StringUtils.EMPTY);
214+
.filter(StringUtils::isNotBlank)
215+
.orElse(null);
132216
}
133217

134218
@Override
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.prebid.server.bidder.missena;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.node.ObjectNode; // Changed import
5+
import lombok.Builder;
6+
import lombok.Value;
7+
8+
import java.util.List;
9+
10+
@Builder
11+
@Value
12+
public class MissenaUserParams {
13+
14+
List<String> formats;
15+
16+
String placement;
17+
18+
@JsonProperty("test")
19+
String testMode;
20+
21+
ObjectNode settings;
22+
}
23+
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
package org.prebid.server.proto.openrtb.ext.request.missena;
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.node.ObjectNode;
5+
import lombok.Builder;
46
import lombok.Value;
57

6-
@Value(staticConstructor = "of")
8+
import java.util.List;
9+
10+
@Value
11+
@Builder(toBuilder = true)
712
public class ExtImpMissena {
813

914
@JsonProperty("apiKey")
1015
String apiKey;
1116

17+
List<String> formats;
18+
1219
String placement;
1320

1421
@JsonProperty("test")
1522
String testMode;
23+
24+
ObjectNode settings;
1625
}

src/main/java/org/prebid/server/spring/config/bidder/MissenaConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import org.prebid.server.bidder.BidderDeps;
44
import org.prebid.server.bidder.missena.MissenaBidder;
5+
import org.prebid.server.currency.CurrencyConversionService;
56
import org.prebid.server.json.JacksonMapper;
67
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
78
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
89
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
910
import org.prebid.server.spring.env.YamlPropertySourceFactory;
11+
import org.prebid.server.version.PrebidVersionProvider;
1012
import org.springframework.beans.factory.annotation.Value;
1113
import org.springframework.boot.context.properties.ConfigurationProperties;
1214
import org.springframework.context.annotation.Bean;
@@ -30,12 +32,15 @@ BidderConfigurationProperties configurationProperties() {
3032
@Bean
3133
BidderDeps missenaBidderDeps(BidderConfigurationProperties missenaConfigurationProperties,
3234
@NotBlank @Value("${external-url}") String externalUrl,
35+
CurrencyConversionService currencyConversionService,
36+
PrebidVersionProvider prebidVersionProvider,
3337
JacksonMapper mapper) {
3438

3539
return BidderDepsAssembler.forBidder(BIDDER_NAME)
3640
.withConfig(missenaConfigurationProperties)
3741
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38-
.bidderCreator(config -> new MissenaBidder(config.getEndpoint(), mapper))
42+
.bidderCreator(config -> new MissenaBidder(
43+
config.getEndpoint(), mapper, currencyConversionService, prebidVersionProvider))
3944
.assemble();
4045
}
4146
}

src/main/resources/bidder-config/missena.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
adapters:
22
missena:
3-
endpoint: https://bid.missena.io/
3+
endpoint: https://bid.missena.io/?t={{PublisherID}}
44
meta-info:
55
maintainer-email: prebid@missena.com
66
modifying-vast-xml-allowed: true

src/main/resources/static/bidder-params/missena.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616
"test": {
1717
"type": "string",
1818
"description": "Test Mode"
19+
},
20+
"formats": {
21+
"type": "array",
22+
"description": "An array of formats to request (banner, native, or video)",
23+
"items": {
24+
"type": "string"
25+
}
26+
},
27+
"settings": {
28+
"type": "object",
29+
"description": "An object containing extra settings for the Missena adapter"
1930
}
2031
},
2132
"required": [

0 commit comments

Comments
 (0)