Skip to content
Open
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
39 changes: 36 additions & 3 deletions src/main/java/org/prebid/server/bidder/floxis/FloxisBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
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;
Expand All @@ -23,6 +25,7 @@
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.Uri;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -43,12 +46,18 @@ public class FloxisBidder implements Bidder<BidRequest> {

private static final String DEFAULT_REGION = "us-e";
private static final String DEFAULT_PARTNER = "floxis";
private static final String BIDDER_CURRENCY = "USD";

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

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

this.endpointUrl = Uri.of(endpointUrl);
this.currencyConversionService = Objects.requireNonNull(currencyConversionService);
this.mapper = Objects.requireNonNull(mapper);
}

Expand All @@ -59,19 +68,22 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request

for (Imp imp : request.getImp()) {
final ExtImpFloxis impExt;
final Imp modifiedImp;
try {
impExt = parseImpExt(imp);
modifiedImp = modifyImp(imp, request);
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
continue;
}

impsByUrl.computeIfAbsent(resolveUrl(impExt), key -> new ArrayList<>()).add(imp);
impsByUrl.computeIfAbsent(resolveUrl(impExt), key -> new ArrayList<>()).add(modifiedImp);
}

final List<String> currencies = resolveCurrencies(request.getCur());
final List<HttpRequest<BidRequest>> httpRequests = impsByUrl.entrySet().stream()
.map(entry -> BidderUtil.defaultRequest(
request.toBuilder().imp(entry.getValue()).build(),
request.toBuilder().imp(entry.getValue()).cur(currencies).build(),
entry.getKey(),
mapper))
.toList();
Expand All @@ -97,6 +109,27 @@ private ExtImpFloxis parseImpExt(Imp imp) {
return ExtImpFloxis.of(impExt.getSeat(), region, partner);
}

private Imp modifyImp(Imp imp, BidRequest request) {
final Price bidFloorPrice = Price.of(imp.getBidfloorcur(), imp.getBidfloor());
if (!BidderUtil.shouldConvertBidFloor(bidFloorPrice, BIDDER_CURRENCY)) {
return imp;
}

final BigDecimal convertedPrice = currencyConversionService.convertCurrency(
bidFloorPrice.getValue(), request, bidFloorPrice.getCurrency(), BIDDER_CURRENCY);

return imp.toBuilder()
.bidfloor(convertedPrice)
.bidfloorcur(BIDDER_CURRENCY)
.build();
}

private static List<String> resolveCurrencies(List<String> currencies) {
return CollectionUtils.isEmpty(currencies) || currencies.contains(BIDDER_CURRENCY)
? currencies
: Collections.singletonList(BIDDER_CURRENCY);
}

private String resolveUrl(ExtImpFloxis impExt) {
return endpointUrl
.replaceMacro(HOST_MACRO, resolveBidHost(impExt))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.floxis.FloxisBidder;
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;
Expand All @@ -25,11 +26,12 @@ BidderConfigurationProperties configurationProperties() {

@Bean
BidderDeps floxisBidderDeps(BidderConfigurationProperties floxisConfigurationProperties,
CurrencyConversionService currencyConversionService,
JacksonMapper mapper) {

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(floxisConfigurationProperties)
.bidderCreator(config -> new FloxisBidder(config.getEndpoint(), mapper))
.bidderCreator(config -> new FloxisBidder(config.getEndpoint(), currencyConversionService, mapper))
.assemble();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,24 @@
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.prebid.server.VertxTest;
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.HttpResponse;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.currency.CurrencyConversionService;
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.floxis.ExtImpFloxis;
import org.prebid.server.proto.openrtb.ext.response.BidType;

import java.math.BigDecimal;
import java.util.List;
import java.util.function.UnaryOperator;

Expand All @@ -33,17 +39,29 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;

@ExtendWith(MockitoExtension.class)
public class FloxisBidderTest extends VertxTest {

private static final String ENDPOINT_URL = "https://{Host}.floxis.tech/pbs?seat={SeatId}";

private final FloxisBidder target = new FloxisBidder(ENDPOINT_URL, jacksonMapper);
@Mock
private CurrencyConversionService currencyConversionService;

private FloxisBidder target;

@BeforeEach
public void setUp() {
target = new FloxisBidder(ENDPOINT_URL, currencyConversionService, jacksonMapper);
}

@Test
public void creationShouldFailOnInvalidEndpointUrl() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new FloxisBidder("invalid_url", jacksonMapper));
.isThrownBy(() -> new FloxisBidder("invalid_url", currencyConversionService, jacksonMapper));
}

@Test
Expand Down Expand Up @@ -158,7 +176,7 @@ public void makeHttpRequestsShouldUsePartnerPrefixedHostWhenPartnerProvided() {
@Test
public void makeHttpRequestsShouldLeaveEndpointUnchangedWhenItDeclaresNoMacros() {
// given
final FloxisBidder bidder = new FloxisBidder("http://localhost:8090/floxis-exchange", jacksonMapper);
final FloxisBidder bidder = new FloxisBidder("http://localhost:8090/floxis-exchange", currencyConversionService, jacksonMapper);
final BidRequest bidRequest = givenBidRequest(imp -> imp.ext(givenImpExt("abc", "eu")));

// when
Expand All @@ -174,7 +192,7 @@ public void makeHttpRequestsShouldLeaveEndpointUnchangedWhenItDeclaresNoMacros()
@Test
public void makeHttpRequestsShouldIgnoreRegionAndPartnerWhenAliasEndpointDeclaresNoHostMacro() {
// given
final FloxisBidder bidder = new FloxisBidder("https://hb.adapex.io/pbs?seat={SeatId}", jacksonMapper);
final FloxisBidder bidder = new FloxisBidder("https://hb.adapex.io/pbs?seat={SeatId}", currencyConversionService, jacksonMapper);
final BidRequest bidRequest = givenBidRequest(imp -> imp.ext(givenImpExt("abc", "eu", "acme")));

// when
Expand All @@ -190,7 +208,7 @@ public void makeHttpRequestsShouldIgnoreRegionAndPartnerWhenAliasEndpointDeclare
@Test
public void makeHttpRequestsShouldRouteOnceWhenImpsDifferOnlyInRegionAndPartnerOnAliasEndpoint() {
// given
final FloxisBidder bidder = new FloxisBidder("https://hb.adapex.io/pbs?seat={SeatId}", jacksonMapper);
final FloxisBidder bidder = new FloxisBidder("https://hb.adapex.io/pbs?seat={SeatId}", currencyConversionService, jacksonMapper);
final BidRequest bidRequest = BidRequest.builder()
.id("req-1")
.imp(asList(
Expand Down Expand Up @@ -405,6 +423,36 @@ public void makeHttpRequestsShouldForwardRequestBodyUnchanged() {
.containsExactly(bidRequest);
}

@Test
public void makeHttpRequestsShouldConvertBidFloorAndRequestCurrencyToUsd() {
// given
given(currencyConversionService.convertCurrency(any(), any(), anyString(), anyString()))
.willReturn(BigDecimal.TEN);

final BidRequest bidRequest = givenBidRequest(imp -> imp
.bidfloor(BigDecimal.ONE)
.bidfloorcur("EUR")
.ext(givenImpExt("abc", "us-e")))
.toBuilder()
.cur(singletonList("EUR"))
.build();

// when
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getCur)
.containsExactly(singletonList("USD"));
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.flatExtracting(BidRequest::getImp)
.extracting(Imp::getBidfloor, Imp::getBidfloorcur)
.containsExactly(tuple(BigDecimal.TEN, "USD"));
}

@Test
public void makeBidsShouldReturnErrorWhenResponseBodyCouldNotBeParsed() {
// given
Expand Down