Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions src/main/java/org/prebid/server/bidder/nexx360/Nexx360Bidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package org.prebid.server.bidder.nexx360;

import com.fasterxml.jackson.core.type.TypeReference;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import org.prebid.server.bidder.Bidder;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderCall;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.json.DecodeException;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
import org.prebid.server.proto.openrtb.ext.request.nexx360.ExtImpNexx360;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.HttpUtil;
import org.prebid.server.version.PrebidVersionProvider;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class Nexx360Bidder implements Bidder<BidRequest> {

private static final TypeReference<ExtPrebid<?, ExtImpNexx360>> TYPE_REFERENCE = new TypeReference<>() {
};
private static final String BIDDER_NAME = "nexx360";

private final String endpointUrl;
private final JacksonMapper mapper;
private final PrebidVersionProvider prebidVersionProvider;

public Nexx360Bidder(String endpointUrl, JacksonMapper mapper, PrebidVersionProvider prebidVersionProvider) {
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
this.mapper = Objects.requireNonNull(mapper);
this.prebidVersionProvider = Objects.requireNonNull(prebidVersionProvider);
}

@Override
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
final List<Imp> imps = request.getImp();
final List<Imp> modifiedImps = new ArrayList<>();

final ExtImpNexx360 firstExtImp;
try {
firstExtImp = parseImpExt(imps.getFirst());
} catch (PreBidException e) {
return Result.withError(BidderError.badInput(e.getMessage()));
}

for (final Imp imp : imps) {
modifiedImps.add(modifyImp(imp));
}

final BidRequest modifiedRequest = makeRequest(request, modifiedImps);
final String url = makeUrl(firstExtImp.getTagId(), firstExtImp.getPlacement());
return Result.withValue(BidderUtil.defaultRequest(modifiedRequest, url, mapper));
}

private ExtImpNexx360 parseImpExt(Imp imp) {
try {
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage());
}
}

private Imp modifyImp(Imp imp) {
return imp.toBuilder()
.ext(mapper.mapper().createObjectNode().set(BIDDER_NAME, imp.getExt().get("bidder")))
.build();
}

private BidRequest makeRequest(BidRequest request, List<Imp> imps) {
final ExtRequest extRequest = ExtRequest.empty();
extRequest.addProperty(BIDDER_NAME, mapper.mapper().valueToTree(
Nexx360ExtRequest.of(Nexx360ExtRequestCaller.of(prebidVersionProvider.getNameVersionRecord()))));

return request.toBuilder()
.imp(imps)
.ext(extRequest)
.build();
}

private String makeUrl(String tagId, String placement) {
final URIBuilder uriBuilder;
try {
uriBuilder = new URIBuilder(endpointUrl);
} catch (URISyntaxException e) {
throw new PreBidException("Invalid url: %s, error: %s".formatted(endpointUrl, e.getMessage()));
}

if (StringUtils.isNotBlank(placement)) {
uriBuilder.addParameter("placement", placement);
}
if (StringUtils.isNotBlank(tagId)) {
uriBuilder.addParameter("tag_id", tagId);
}

return uriBuilder.toString();
}

@Override
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
final List<BidderError> errors = new ArrayList<>();
return Result.of(extractBids(bidResponse, errors), errors);
} catch (DecodeException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}
return bidsFromResponse(bidResponse, errors);
}

private List<BidderBid> bidsFromResponse(BidResponse bidResponse, List<BidderError> errors) {
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

private BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
try {
return BidderBid.of(bid, getBidType(bid), currency);
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}
}

private BidType getBidType(Bid bid) {
final String bidType;
try {
bidType = mapper.mapper()
.convertValue(bid.getExt(), Nexx360ExtBid.class)
.getBidType();
} catch (IllegalArgumentException e) {
throw new PreBidException(
"unable to fetch mediaType in multi-format: " + bid.getImpid());
}

return switch (bidType) {
case "banner" -> BidType.banner;
case "video" -> BidType.video;
case "audio" -> BidType.audio;
case "native" -> BidType.xNative;
default -> throw new PreBidException(
"unable to fetch mediaType in multi-format: " + bid.getImpid());
};
}
}
11 changes: 11 additions & 0 deletions src/main/java/org/prebid/server/bidder/nexx360/Nexx360ExtBid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prebid.server.bidder.nexx360;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;

@Value(staticConstructor = "of")
public class Nexx360ExtBid {

@JsonProperty("bidType")
String bidType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.prebid.server.bidder.nexx360;

import lombok.Value;

import java.util.Collections;
import java.util.List;

@Value(staticConstructor = "of")
public class Nexx360ExtRequest {

List<Nexx360ExtRequestCaller> caller;

public static Nexx360ExtRequest of(Nexx360ExtRequestCaller caller) {
return of(Collections.singletonList(caller));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prebid.server.bidder.nexx360;

import lombok.Value;

@Value(staticConstructor = "of")
public class Nexx360ExtRequestCaller {

String name;

String version;

public static Nexx360ExtRequestCaller of(String version) {
return Nexx360ExtRequestCaller.of("Prebid-Server", version);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.prebid.server.proto.openrtb.ext.request.nexx360;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;

@Value(staticConstructor = "of")
public class ExtImpNexx360 {

@JsonProperty("tagId")
String tagId;

String placement;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.prebid.server.spring.config.bidder;

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.nexx360.Nexx360Bidder;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
import org.prebid.server.spring.env.YamlPropertySourceFactory;
import org.prebid.server.version.PrebidVersionProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import jakarta.validation.constraints.NotBlank;

@Configuration
@PropertySource(value = "classpath:/bidder-config/nexx360.yaml", factory = YamlPropertySourceFactory.class)
public class Nexx360Configuration {

private static final String BIDDER_NAME = "nexx360";

@Bean("nexx360ConfigurationProperties")
@ConfigurationProperties("adapters.nexx360")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps nexx360BidderDeps(BidderConfigurationProperties nexx360ConfigurationProperties,
@NotBlank @Value("${external-url}") String externalUrl,
PrebidVersionProvider prebidVersionProvider,
JacksonMapper mapper) {

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(nexx360ConfigurationProperties)
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
.bidderCreator(config -> new Nexx360Bidder(
config.getEndpoint(),
mapper,
prebidVersionProvider))
.assemble();
}
}
22 changes: 22 additions & 0 deletions src/main/resources/bidder-config/nexx360.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
adapters:
nexx360:
endpoint: http://fast.nexx360.io/prebid-server
endpoint-compression: gzip
aliases:
1accord: ~
easybid: ~
prismassp: ~
meta-info:
maintainer-email: tech@nexx360.io
app-media-types:
- banner
- video
- native
- audio
site-media-types:
- banner
- video
- native
- audio
supported-vendors:
vendor-id: 0
30 changes: 30 additions & 0 deletions src/main/resources/static/bidder-params/nexx360.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Nexx360 Adapter Params",
"description": "A schema which validates params accepted by the Nexx360 adapter",
"type": "object",
"properties": {
"tagId": {
"type": "string",
"minLength": 1,
"description": "TagId"
},
"placement": {
"type": "string",
"minLength": 1,
"description": "Placement"
}
},
"anyOf": [
{
"required": [
"tagId"
]
},
{
"required": [
"placement"
]
}
]
}
Loading
Loading