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
101 changes: 101 additions & 0 deletions src/main/java/org/prebid/server/bidder/adagio/AdagioBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.prebid.server.bidder.adagio;

import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import org.apache.commons.collections4.CollectionUtils;
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.response.BidType;
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.HttpUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class AdagioBidder implements Bidder<BidRequest> {

private final String endpointUrl;
private final JacksonMapper mapper;

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

@Override
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
return Result.withValue(BidderUtil.defaultRequest(request, endpointUrl, mapper));
}

@Override
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
final List<BidderError> errors = new ArrayList<>();
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
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())) {
errors.add(BidderError.badServerResponse("empty seatbid array"));
return Collections.emptyList();
}

return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.flatMap(seatBid -> seatBid.getBid().stream()
.filter(Objects::nonNull)
.map(bid -> makeBid(bid, bidResponse.getCur(), seatBid.getSeat(), errors)))
.filter(Objects::nonNull)
.toList();
}

private BidderBid makeBid(Bid bid, String currency, String seat, List<BidderError> errors) {
try {
final ExtBidPrebid extBidPrebid = parseBidExt(bid);
return BidderBid.builder()
.bid(bid)
.type(getBidType(bid))
.bidCurrency(currency)
.videoInfo(extBidPrebid != null ? extBidPrebid.getVideo() : null)
.seat(seat)
.build();

} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}
}

private ExtBidPrebid parseBidExt(Bid bid) {
try {
return mapper.mapper().convertValue(bid.getExt(), ExtBidPrebid.class);
} catch (IllegalArgumentException e) {
throw new PreBidException("bid.ext can not be parsed");
}
}

private static BidType getBidType(Bid bid) {
return switch (bid.getMtype()) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
case 4 -> BidType.xNative;
case null, default -> throw new PreBidException(
"Could not define media type for impression: " + bid.getImpid());
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.prebid.server.proto.openrtb.ext.request.adagio;

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

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

@JsonProperty("organizationId")
String organizationId;

String placement;

String pagetype;

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

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.adagio.AdagioBidder;
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.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/adagio.yaml", factory = YamlPropertySourceFactory.class)
public class AdagioConfiguration {

private static final String BIDDER_NAME = "adagio";

@Bean("adagioConfigurationProperties")
@ConfigurationProperties("adapters.adagio")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps adagioBidderDeps(BidderConfigurationProperties adagioConfigurationProperties,
@NotBlank @Value("${external-url}") String externalUrl,
JacksonMapper mapper) {

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(adagioConfigurationProperties)
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
.bidderCreator(config -> new AdagioBidder(config.getEndpoint(), mapper))
.assemble();
}
}
22 changes: 22 additions & 0 deletions src/main/resources/bidder-config/adagio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
adapters:
adagio:
# Please deploy this config in each of your datacenters with the appropriate regional subdomain.
# Replace the `REGION` by one of the value below:
# - For AMER: las => (https://mp-las.4dex.io/pbserver)
# - For EMEA: ams => (https://mp-ams.4dex.io/pbserver)
# - For APAC: tyo => (https://mp-tyo.4dex.io/pbserver)
endpoint: https://mp-REGION.4dex.io/pbserver
ortb-version: "2.6"
endpoint-compression: gzip
meta-info:
maintainer-email: dev@adagio.io
app-media-types:
- banner
- video
- native
site-media-types:
- banner
- video
- native
supported-vendors:
vendor-id: 617
31 changes: 31 additions & 0 deletions src/main/resources/static/bidder-params/adagio.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Adagio Adapter Params",
"description": "A schema which validates params accepted by the Adagio adapter",
"type": "object",
"properties": {
"organizationId": {
"type": "string",
"description": "Id of the Organization. Handed out by Adagio."
},
"placement": {
"type": "string",
"description": "Refers to the placement of an adunit in a page. Must not contain any information about the type of device.",
"maxLength": 30
},
"pagetype": {
"type": "string",
"description": "Describes what kind of content will be present in the page.",
"maxLength": 30
},
"category": {
"type": "string",
"description": "Category of the content displayed in the page.",
"maxLength": 30
}
},
"required": [
"organizationId",
"placement"
]
}
Loading
Loading