Skip to content

Commit 71ccdef

Browse files
committed
Port Mobkoi: New Adapter
1 parent 2ebf421 commit 71ccdef

12 files changed

Lines changed: 669 additions & 0 deletions

File tree

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package org.prebid.server.bidder.mobkoi;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.node.ObjectNode;
5+
import com.iab.openrtb.request.BidRequest;
6+
import com.iab.openrtb.request.Imp;
7+
import com.iab.openrtb.response.Bid;
8+
import com.iab.openrtb.response.BidResponse;
9+
import com.iab.openrtb.response.SeatBid;
10+
import org.apache.commons.collections4.CollectionUtils;
11+
import org.prebid.server.bidder.Bidder;
12+
import org.prebid.server.bidder.model.BidderBid;
13+
import org.prebid.server.bidder.model.BidderCall;
14+
import org.prebid.server.bidder.model.BidderError;
15+
import org.prebid.server.bidder.model.HttpRequest;
16+
import org.prebid.server.bidder.model.Result;
17+
import org.prebid.server.exception.PreBidException;
18+
import org.prebid.server.json.DecodeException;
19+
import org.prebid.server.json.JacksonMapper;
20+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
21+
import org.prebid.server.proto.openrtb.ext.request.mobkoi.ExtImpMobkoi;
22+
import org.prebid.server.proto.openrtb.ext.response.BidType;
23+
import org.prebid.server.util.BidderUtil;
24+
import org.prebid.server.util.HttpUtil;
25+
26+
import java.util.Collection;
27+
import java.util.Collections;
28+
import java.util.List;
29+
import java.util.Objects;
30+
import java.util.Optional;
31+
import java.util.stream.Collectors;
32+
33+
public class MobkoiBidder implements Bidder<BidRequest> {
34+
35+
private static final TypeReference<ExtPrebid<?, ExtImpMobkoi>> MOBKOI_EXT_TYPE_REFERENCE =
36+
new TypeReference<>() {
37+
};
38+
39+
private final String endpointUrl;
40+
private final JacksonMapper mapper;
41+
42+
public MobkoiBidder(String endpointUrl, JacksonMapper mapper) {
43+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
44+
this.mapper = Objects.requireNonNull(mapper);
45+
}
46+
47+
@Override
48+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest bidRequest) {
49+
50+
final Imp firstImp = bidRequest.getImp().stream().findFirst().orElse(null);
51+
if (firstImp == null) {
52+
return Result.withError(BidderError.badInput("No impression provided"));
53+
}
54+
55+
final ExtImpMobkoi extImpMobkoi;
56+
try {
57+
extImpMobkoi = parseExtImp(firstImp);
58+
} catch (PreBidException e) {
59+
return Result.withError(BidderError.badInput(e.getMessage()));
60+
}
61+
62+
if (extImpMobkoi.getPlacementId() == null) {
63+
return Result.withError(BidderError.badInput("placementId should not be null"));
64+
}
65+
66+
final String selectedEndpointUrl = Optional.ofNullable(extImpMobkoi.getAdServerBaseUrl())
67+
.filter(this::validCustomEndpointUrl)
68+
.orElse(endpointUrl);
69+
70+
final List<Imp> modifiedImps = List.of(modifyImp(firstImp, extImpMobkoi));
71+
72+
return Result.withValue(BidderUtil.defaultRequest(
73+
modifyBidRequest(bidRequest, modifiedImps),
74+
selectedEndpointUrl,
75+
mapper));
76+
}
77+
78+
private ExtImpMobkoi parseExtImp(Imp imp) {
79+
try {
80+
return mapper.mapper().convertValue(imp.getExt(), MOBKOI_EXT_TYPE_REFERENCE).getBidder();
81+
} catch (IllegalArgumentException e) {
82+
throw new PreBidException(
83+
"Invalid imp.ext for impression id %s. Error Information: %s"
84+
.formatted(imp.getId(), e.getMessage()));
85+
}
86+
}
87+
88+
private Boolean validCustomEndpointUrl(String customUrl) {
89+
try {
90+
HttpUtil.validateUrl(Objects.requireNonNull(customUrl));
91+
return true;
92+
} catch (IllegalArgumentException | NullPointerException e) {
93+
return false;
94+
}
95+
}
96+
97+
private Imp modifyImp(Imp imp, ExtImpMobkoi extImpMobkoi) {
98+
final ObjectNode ext = mapper.mapper().createObjectNode();
99+
ext.set("bidder", mapper.mapper().valueToTree(extImpMobkoi));
100+
return imp.toBuilder().ext(ext).build();
101+
}
102+
103+
private static BidRequest modifyBidRequest(BidRequest bidRequest, List<Imp> imps) {
104+
return bidRequest.toBuilder().imp(imps).build();
105+
}
106+
107+
@Override
108+
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
109+
try {
110+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
111+
return Result.withValues(extractBids(bidResponse));
112+
} catch (DecodeException | PreBidException e) {
113+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
114+
}
115+
}
116+
117+
private static List<BidderBid> extractBids(BidResponse bidResponse) {
118+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
119+
return Collections.emptyList();
120+
}
121+
return bidsFromResponse(bidResponse);
122+
}
123+
124+
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse) {
125+
return bidResponse.getSeatbid()
126+
.stream()
127+
.filter(Objects::nonNull)
128+
.map(SeatBid::getBid)
129+
.filter(Objects::nonNull)
130+
.flatMap(Collection::stream)
131+
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
132+
.collect(Collectors.toList());
133+
}
134+
135+
private static BidType getBidType(Bid bid) {
136+
final Integer markupType = bid.getMtype();
137+
if (markupType == null) {
138+
throw new PreBidException("Missing mediaType for bid: " + bid.getId());
139+
}
140+
141+
if (markupType != 1) {
142+
throw new PreBidException(
143+
"Unsupported bid mediaType: %s for impression: %s"
144+
.formatted(markupType, bid.getImpid()));
145+
}
146+
147+
return BidType.banner;
148+
}
149+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.prebid.server.proto.openrtb.ext.request.mobkoi;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
/**
7+
* Defines the contract for bidrequest.imp[i].ext.mobkoi
8+
*/
9+
@Value(staticConstructor = "of")
10+
public class ExtImpMobkoi {
11+
12+
@JsonProperty("placementId")
13+
String placementId;
14+
15+
@JsonProperty("adServerBaseUrl")
16+
String adServerBaseUrl;
17+
}
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.mobkoi.MobkoiBidder;
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 javax.validation.constraints.NotBlank;
17+
18+
@Configuration
19+
@PropertySource(value = "classpath:/bidder-config/mobkoi.yaml", factory = YamlPropertySourceFactory.class)
20+
public class MobkoiConfiguration {
21+
22+
private static final String BIDDER_NAME = "mobkoi";
23+
24+
@Bean("mobkoiConfigurationProperties")
25+
@ConfigurationProperties("adapters.mobkoi")
26+
BidderConfigurationProperties configurationProperties() {
27+
return new BidderConfigurationProperties();
28+
}
29+
30+
@Bean
31+
BidderDeps mobkoiBidderDeps(BidderConfigurationProperties mobkoiConfigurationProperties,
32+
@NotBlank @Value("${external-url}") String externalUrl,
33+
JacksonMapper mapper) {
34+
35+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
36+
.withConfig(mobkoiConfigurationProperties)
37+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38+
.bidderCreator(config -> new MobkoiBidder(config.getEndpoint(), mapper))
39+
.assemble();
40+
}
41+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
adapters:
2+
mobkoi:
3+
endpoint: "https://pbs.maximus.mobkoi.com/bid"
4+
meta-info:
5+
maintainer-email: platformteam@mobkoi.com
6+
app-media-types:
7+
site-media-types:
8+
- banner
9+
supported-vendors:
10+
vendor-id: 898
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Mobkoi Adapter Params",
4+
"description": "A schema which validates params accepted by the Mobkoi adapter",
5+
"type": "object",
6+
"properties": {
7+
"placementId": {
8+
"type": "string",
9+
"description": "Placement ID"
10+
},
11+
"adServerBaseUrl": {
12+
"type": "string",
13+
"description": "Mobkoi's ad server url",
14+
"pattern": "^https?://[^.]+\\.mobkoi\\.com$"
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)