Skip to content

Commit 1dd6e6b

Browse files
New Nexx360 Adapter (#4053)
1 parent 4ebd73e commit 1dd6e6b

30 files changed

+1342
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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> imps = request.getImp();
56+
final List<Imp> modifiedImps = new ArrayList<>();
57+
58+
final ExtImpNexx360 firstExtImp;
59+
try {
60+
firstExtImp = parseImpExt(imps.getFirst());
61+
} catch (PreBidException e) {
62+
return Result.withError(BidderError.badInput(e.getMessage()));
63+
}
64+
65+
for (final Imp imp : imps) {
66+
modifiedImps.add(modifyImp(imp));
67+
}
68+
69+
final BidRequest modifiedRequest = makeRequest(request, modifiedImps);
70+
final String url = makeUrl(firstExtImp.getTagId(), firstExtImp.getPlacement());
71+
return Result.withValue(BidderUtil.defaultRequest(modifiedRequest, url, mapper));
72+
}
73+
74+
private ExtImpNexx360 parseImpExt(Imp imp) {
75+
try {
76+
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
77+
} catch (IllegalArgumentException e) {
78+
throw new PreBidException(e.getMessage());
79+
}
80+
}
81+
82+
private Imp modifyImp(Imp imp) {
83+
return imp.toBuilder()
84+
.ext(mapper.mapper().createObjectNode().set(BIDDER_NAME, imp.getExt().get("bidder")))
85+
.build();
86+
}
87+
88+
private BidRequest makeRequest(BidRequest request, List<Imp> imps) {
89+
final ExtRequest extRequest = ExtRequest.empty();
90+
extRequest.addProperty(BIDDER_NAME, mapper.mapper().valueToTree(
91+
Nexx360ExtRequest.of(Nexx360ExtRequestCaller.of(prebidVersionProvider.getNameVersionRecord()))));
92+
93+
return request.toBuilder()
94+
.imp(imps)
95+
.ext(extRequest)
96+
.build();
97+
}
98+
99+
private String makeUrl(String tagId, String placement) {
100+
final URIBuilder uriBuilder;
101+
try {
102+
uriBuilder = new URIBuilder(endpointUrl);
103+
} catch (URISyntaxException e) {
104+
throw new PreBidException("Invalid url: %s, error: %s".formatted(endpointUrl, e.getMessage()));
105+
}
106+
107+
if (StringUtils.isNotBlank(placement)) {
108+
uriBuilder.addParameter("placement", placement);
109+
}
110+
if (StringUtils.isNotBlank(tagId)) {
111+
uriBuilder.addParameter("tag_id", tagId);
112+
}
113+
114+
return uriBuilder.toString();
115+
}
116+
117+
@Override
118+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
119+
try {
120+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
121+
final List<BidderError> errors = new ArrayList<>();
122+
return Result.of(extractBids(bidResponse, errors), errors);
123+
} catch (DecodeException e) {
124+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
125+
}
126+
}
127+
128+
private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
129+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
130+
return Collections.emptyList();
131+
}
132+
return bidsFromResponse(bidResponse, errors);
133+
}
134+
135+
private List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> errors) {
136+
return bidResponse.getSeatbid().stream()
137+
.filter(Objects::nonNull)
138+
.map(SeatBid::getBid)
139+
.filter(Objects::nonNull)
140+
.flatMap(Collection::stream)
141+
.filter(Objects::nonNull)
142+
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
143+
.filter(Objects::nonNull)
144+
.collect(Collectors.toList());
145+
}
146+
147+
private BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
148+
try {
149+
return BidderBid.of(bid, getBidType(bid), currency);
150+
} catch (PreBidException e) {
151+
errors.add(BidderError.badServerResponse(e.getMessage()));
152+
return null;
153+
}
154+
}
155+
156+
private BidType getBidType(Bid bid) {
157+
final String bidType;
158+
try {
159+
bidType = mapper.mapper()
160+
.convertValue(bid.getExt(), Nexx360ExtBid.class)
161+
.getBidType();
162+
} catch (IllegalArgumentException e) {
163+
throw new PreBidException(
164+
"unable to fetch mediaType in multi-format: " + bid.getImpid());
165+
}
166+
167+
return switch (bidType) {
168+
case "banner" -> BidType.banner;
169+
case "video" -> BidType.video;
170+
case "audio" -> BidType.audio;
171+
case "native" -> BidType.xNative;
172+
default -> throw new PreBidException(
173+
"unable to fetch mediaType in multi-format: " + bid.getImpid());
174+
};
175+
}
176+
}
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)