Skip to content

Commit c9a377f

Browse files
committed
New Nexx360 Adapter
1 parent 9d07434 commit c9a377f

30 files changed

Lines changed: 1346 additions & 0 deletions
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package org.prebid.server.bidder.nexx360;
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 org.apache.commons.collections4.CollectionUtils;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.apache.http.client.utils.URIBuilder;
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.ExtRequest;
23+
import org.prebid.server.proto.openrtb.ext.request.nexx360.ExtImpNexx360;
24+
import org.prebid.server.proto.openrtb.ext.response.BidType;
25+
import org.prebid.server.util.BidderUtil;
26+
import org.prebid.server.util.HttpUtil;
27+
import org.prebid.server.version.PrebidVersionProvider;
28+
29+
import java.net.URISyntaxException;
30+
import java.util.ArrayList;
31+
import java.util.Collection;
32+
import java.util.Collections;
33+
import java.util.List;
34+
import java.util.Objects;
35+
import java.util.stream.Collectors;
36+
37+
public class Nexx360Bidder implements Bidder<BidRequest> {
38+
39+
private static final TypeReference<ExtPrebid<?, ExtImpNexx360>> TYPE_REFERENCE = new TypeReference<>() {
40+
};
41+
private static final String BIDDER_NAME = "nexx360";
42+
43+
private final String endpointUrl;
44+
private final JacksonMapper mapper;
45+
private final PrebidVersionProvider prebidVersionProvider;
46+
47+
public Nexx360Bidder(String endpointUrl, JacksonMapper mapper, PrebidVersionProvider prebidVersionProvider) {
48+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
49+
this.mapper = Objects.requireNonNull(mapper);
50+
this.prebidVersionProvider = Objects.requireNonNull(prebidVersionProvider);
51+
}
52+
53+
@Override
54+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
55+
final List<Imp> modifiedImps = new ArrayList<>();
56+
57+
String tagId = null;
58+
String placement = null;
59+
60+
try {
61+
final List<Imp> imps = request.getImp();
62+
for (int i = 0; i < imps.size(); i++) {
63+
final Imp imp = imps.get(i);
64+
final ExtImpNexx360 extImp = parseBidderExt(imp);
65+
final Imp modifiedImp = imp.toBuilder()
66+
.ext(mapper.mapper().createObjectNode().set(BIDDER_NAME, mapper.mapper().valueToTree(extImp)))
67+
.build();
68+
modifiedImps.add(modifiedImp);
69+
70+
if (i == 0) {
71+
tagId = extImp.getTagId();
72+
placement = extImp.getPlacement();
73+
}
74+
}
75+
} catch (PreBidException e) {
76+
return Result.withError(BidderError.badInput(e.getMessage()));
77+
}
78+
79+
final BidRequest modifiedRequest = makeRequest(request, modifiedImps);
80+
final String url = makeUrl(tagId, placement);
81+
return Result.withValue(BidderUtil.defaultRequest(modifiedRequest, url, mapper));
82+
}
83+
84+
private ExtImpNexx360 parseBidderExt(Imp imp) {
85+
try {
86+
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
87+
} catch (IllegalArgumentException e) {
88+
throw new PreBidException(e.getMessage());
89+
}
90+
}
91+
92+
private BidRequest makeRequest(BidRequest request, List<Imp> imps) {
93+
final ExtRequest extRequest = ExtRequest.empty();
94+
extRequest.addProperty(BIDDER_NAME, mapper.mapper().valueToTree(
95+
Nexx360ExtRequest.of(Nexx360ExtRequestCaller.of(prebidVersionProvider.getNameVersionRecord()))));
96+
97+
return request.toBuilder()
98+
.imp(imps)
99+
.ext(extRequest)
100+
.build();
101+
}
102+
103+
private String makeUrl(String tagId, String placement) {
104+
final URIBuilder uriBuilder;
105+
try {
106+
uriBuilder = new URIBuilder(endpointUrl);
107+
} catch (URISyntaxException e) {
108+
throw new PreBidException("Invalid url: %s, error: %s".formatted(endpointUrl, e.getMessage()));
109+
}
110+
111+
if (StringUtils.isNotBlank(placement)) {
112+
uriBuilder.addParameter("placement", placement);
113+
}
114+
if (StringUtils.isNotBlank(tagId)) {
115+
uriBuilder.addParameter("tag_id", tagId);
116+
}
117+
118+
return uriBuilder.toString();
119+
}
120+
121+
@Override
122+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
123+
try {
124+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
125+
final List<BidderError> errors = new ArrayList<>();
126+
return Result.of(extractBids(bidResponse, errors), errors);
127+
} catch (DecodeException e) {
128+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
129+
}
130+
}
131+
132+
private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
133+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
134+
return Collections.emptyList();
135+
}
136+
return bidsFromResponse(bidResponse, errors);
137+
}
138+
139+
private List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> errors) {
140+
return bidResponse.getSeatbid().stream()
141+
.filter(Objects::nonNull)
142+
.map(SeatBid::getBid)
143+
.filter(Objects::nonNull)
144+
.flatMap(Collection::stream)
145+
.filter(Objects::nonNull)
146+
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
147+
.filter(Objects::nonNull)
148+
.collect(Collectors.toList());
149+
}
150+
151+
private BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
152+
try {
153+
return BidderBid.of(bid, getBidType(bid), currency);
154+
} catch (PreBidException e) {
155+
errors.add(BidderError.badServerResponse(e.getMessage()));
156+
return null;
157+
}
158+
}
159+
160+
private BidType getBidType(Bid bid) {
161+
final String bidType;
162+
try {
163+
bidType = mapper.mapper()
164+
.convertValue(bid.getExt(), Nexx360ExtBid.class)
165+
.getBidType();
166+
} catch (IllegalArgumentException e) {
167+
throw new PreBidException(
168+
"unable to fetch mediaType in multi-format: " + bid.getImpid());
169+
}
170+
171+
return switch (bidType) {
172+
case "banner" -> BidType.banner;
173+
case "video" -> BidType.video;
174+
case "audio" -> BidType.audio;
175+
case "native" -> BidType.xNative;
176+
default -> throw new PreBidException(
177+
"unable to fetch mediaType in multi-format: " + bid.getImpid());
178+
};
179+
}
180+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.prebid.server.bidder.nexx360;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class Nexx360ExtBid {
8+
9+
@JsonProperty("bidType")
10+
String bidType;
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.prebid.server.bidder.nexx360;
2+
3+
import lombok.Value;
4+
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
@Value(staticConstructor = "of")
9+
public class Nexx360ExtRequest {
10+
11+
List<Nexx360ExtRequestCaller> caller;
12+
13+
public static Nexx360ExtRequest of(Nexx360ExtRequestCaller caller) {
14+
return of(Collections.singletonList(caller));
15+
}
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.prebid.server.bidder.nexx360;
2+
3+
import lombok.Value;
4+
5+
@Value(staticConstructor = "of")
6+
public class Nexx360ExtRequestCaller {
7+
8+
String name;
9+
10+
String version;
11+
12+
public static Nexx360ExtRequestCaller of(String version) {
13+
return Nexx360ExtRequestCaller.of("Prebid-Server", version);
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.prebid.server.proto.openrtb.ext.request.nexx360;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpNexx360 {
8+
9+
@JsonProperty("tagId")
10+
String tagId;
11+
12+
String placement;
13+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.prebid.server.spring.config.bidder;
2+
3+
import org.prebid.server.bidder.BidderDeps;
4+
import org.prebid.server.bidder.nexx360.Nexx360Bidder;
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.prebid.server.version.PrebidVersionProvider;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.boot.context.properties.ConfigurationProperties;
13+
import org.springframework.context.annotation.Bean;
14+
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.context.annotation.PropertySource;
16+
17+
import jakarta.validation.constraints.NotBlank;
18+
19+
@Configuration
20+
@PropertySource(value = "classpath:/bidder-config/nexx360.yaml", factory = YamlPropertySourceFactory.class)
21+
public class Nexx360Configuration {
22+
23+
private static final String BIDDER_NAME = "nexx360";
24+
25+
@Bean("nexx360ConfigurationProperties")
26+
@ConfigurationProperties("adapters.nexx360")
27+
BidderConfigurationProperties configurationProperties() {
28+
return new BidderConfigurationProperties();
29+
}
30+
31+
@Bean
32+
BidderDeps nexx360BidderDeps(BidderConfigurationProperties nexx360ConfigurationProperties,
33+
@NotBlank @Value("${external-url}") String externalUrl,
34+
PrebidVersionProvider prebidVersionProvider,
35+
JacksonMapper mapper) {
36+
37+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
38+
.withConfig(nexx360ConfigurationProperties)
39+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
40+
.bidderCreator(config -> new Nexx360Bidder(
41+
config.getEndpoint(),
42+
mapper,
43+
prebidVersionProvider))
44+
.assemble();
45+
}
46+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
adapters:
2+
nexx360:
3+
endpoint: http://fast.nexx360.io/prebid-server
4+
endpoint-compression: gzip
5+
aliases:
6+
1accord: ~
7+
easybid: ~
8+
prismassp: ~
9+
meta-info:
10+
maintainer-email: tech@nexx360.io
11+
app-media-types:
12+
- banner
13+
- video
14+
- native
15+
- audio
16+
site-media-types:
17+
- banner
18+
- video
19+
- native
20+
- audio
21+
supported-vendors:
22+
vendor-id: 0
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Nexx360 Adapter Params",
4+
"description": "A schema which validates params accepted by the Nexx360 adapter",
5+
"type": "object",
6+
"properties": {
7+
"tagId": {
8+
"type": "string",
9+
"minLength": 1,
10+
"description": "TagId"
11+
},
12+
"placement": {
13+
"type": "string",
14+
"minLength": 1,
15+
"description": "Placement"
16+
}
17+
},
18+
"anyOf": [
19+
{
20+
"required": [
21+
"tagId"
22+
]
23+
},
24+
{
25+
"required": [
26+
"placement"
27+
]
28+
}
29+
]
30+
}

0 commit comments

Comments
 (0)