Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
152 changes: 152 additions & 0 deletions src/main/java/org/prebid/server/bidder/seedtag/SeedtagBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package org.prebid.server.bidder.seedtag;

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.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.Price;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.currency.CurrencyConversionService;
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.seedtag.ExtImpSeedtag;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.HttpUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class SeedtagBidder implements Bidder<BidRequest> {

private static final TypeReference<ExtPrebid<?, ExtImpSeedtag>> SEEDTAG_EXT_TYPE_REFERENCE =
new TypeReference<>() {
};
private static final String BIDDER_CURRENCY = "USD";

private final String endpointUrl;
private final JacksonMapper mapper;
private final CurrencyConversionService currencyConversionService;

public SeedtagBidder(String endpointUrl,
CurrencyConversionService currencyConversionService,
JacksonMapper mapper) {

this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
this.mapper = Objects.requireNonNull(mapper);
}

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

for (Imp imp : request.getImp()) {
try {
final Price bidFloorPrice = resolveBidFloor(imp, request);

modifiedImps.add(modifyImp(imp, bidFloorPrice));
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
}
}

if (modifiedImps.size() < 1) {
return Result.withErrors(errors);
}

final BidRequest modifiedBidRequest = request.toBuilder()
.imp(modifiedImps)
.build();
return Result.of(
Collections.singletonList(BidderUtil.defaultRequest(modifiedBidRequest, endpointUrl, mapper)),
errors);
}

private static Imp modifyImp(Imp imp, Price bidFloorPrice) {
return imp.toBuilder()
.bidfloorcur(bidFloorPrice.getCurrency())
.bidfloor(bidFloorPrice.getValue())
.build();
}

private Price resolveBidFloor(Imp imp, BidRequest bidRequest) {
final Price initialBidFloorPrice = Price.of(imp.getBidfloorcur(), imp.getBidfloor());
return BidderUtil.shouldConvertBidFloor(initialBidFloorPrice, BIDDER_CURRENCY)
? convertBidFloor(initialBidFloorPrice, imp.getId(), bidRequest)
: initialBidFloorPrice;
}

private Price convertBidFloor(Price bidFloorPrice, String impId, BidRequest bidRequest) {
final BigDecimal convertedPrice = currencyConversionService.convertCurrency(
bidFloorPrice.getValue(),
bidRequest,
bidFloorPrice.getCurrency(),
BIDDER_CURRENCY);

return Price.of(BIDDER_CURRENCY, convertedPrice);
}

@Override
public final 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<>();
final List<BidderBid> bidderBids = extractBids(bidResponse, errors);
return Result.of(bidderBids, errors);
} catch (DecodeException | PreBidException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

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

return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.map(bid -> makeBidderBid(bid, errors))
.filter(Objects::nonNull)
.toList();
}

private static BidderBid makeBidderBid(Bid bid, List<BidderError> errors) {
final BidType bidType;
try {
bidType = getBidType(bid);
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}

return BidderBid.of(bid, bidType, BIDDER_CURRENCY);
}

private static BidType getBidType(Bid bid) {
return switch (bid.getMtype()) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
default -> throw new PreBidException("Invalid bid.mtype for bid.id: '%s'".formatted(bid.getId()));
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.prebid.server.proto.openrtb.ext.request.seedtag;

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

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

@JsonProperty("adUnitId")
String adUnitId;

}
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.seedtag.SeedtagBidder;
import org.prebid.server.currency.CurrencyConversionService;
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/seedtag.yaml", factory = YamlPropertySourceFactory.class)
public class SeedtagConfiguration {

private static final String BIDDER_NAME = "seedtag";

@Bean("seedtagConfigurationProperties")
@ConfigurationProperties("adapters.seedtag")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps seedtagBidderDeps(BidderConfigurationProperties seedtagConfigurationProperties,
@NotBlank @Value("${external-url}") String externalUrl,
CurrencyConversionService currencyConversionService,
JacksonMapper mapper) {

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(seedtagConfigurationProperties)
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
.bidderCreator(config -> new SeedtagBidder(
config.getEndpoint(),
currencyConversionService,
mapper))
.assemble();
}
}
17 changes: 17 additions & 0 deletions src/main/resources/bidder-config/seedtag.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
adapters:
seedtag:
endpoint: "https://s.seedtag.com/c/openrtb?partner=prebidserver"
endpoint-compression: gzip
meta-info:
maintainer-email: [email protected]
site-media-types:
- banner
- video
supported-vendors:
vendor-id: 157
usersync:
cookie-family-name: seedtag
iframe:
url: https://s.seedtag.com/cs/cookiesync/prebid?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&usp_consent={{us_privacy}}&redirect={{redirect_url}}
support-cors: false
uid-macro: '$UID'
16 changes: 16 additions & 0 deletions src/main/resources/static/bidder-params/seedtag.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Seedtag Adapter Params",
"description": "A schema which validates params accepted by the Seedtag adapter",
"type": "object",
"properties": {
"adUnitId": {
"type": "string",
"description": "Ad Unit ID",
"minLength": 1
}
},
"required": [
"adUnitId"
]
}
Loading