From 3195fb74d14503602e38de2805951283ddc08e49 Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Fri, 26 Jun 2020 23:17:04 +0300 Subject: [PATCH 1/9] AdoceanBidder and tests --- .../server/bidder/adocean/AdoceanBidder.java | 303 ++++++++++++++ .../adocean/model/AdoceanResponseAdUnit.java | 32 ++ .../ext/request/adocean/ExtImpAdocean.java | 22 + .../config/bidder/AdoceanConfiguration.java | 53 +++ src/main/resources/bidder-config/adocean.yaml | 22 + .../static/bidder-params/adocean.json | 24 ++ .../bidder/adocean/AdoceanBidderTest.java | 381 ++++++++++++++++++ .../org/prebid/server/it/AdoceanTest.java | 69 ++++ .../adocean/test-adocean-bid-response-1.json | 14 + .../adocean/test-auction-adocean-request.json | 95 +++++ .../test-auction-adocean-response.json | 56 +++ .../adocean/test-cache-adocean-request.json | 16 + .../adocean/test-cache-adocean-response.json | 7 + .../server/it/test-application.properties | 4 + 14 files changed, 1098 insertions(+) create mode 100644 src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java create mode 100644 src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java create mode 100644 src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java create mode 100644 src/main/resources/bidder-config/adocean.yaml create mode 100644 src/main/resources/static/bidder-params/adocean.json create mode 100644 src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java create mode 100644 src/test/java/org/prebid/server/it/AdoceanTest.java create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-response.json diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java new file mode 100644 index 00000000000..94080fd77d9 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -0,0 +1,303 @@ +package org.prebid.server.bidder.adocean; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.User; +import com.iab.openrtb.response.Bid; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpMethod; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.adocean.model.AdoceanResponseAdUnit; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +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.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; +import org.prebid.server.proto.openrtb.ext.request.adocean.ExtImpAdocean; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class AdoceanBidder implements Bidder { + + private static final TypeReference> ADOCEAN_EXT_TYPE_REFERENCE = + new TypeReference>() { + }; + private static final String VERSION = "1.0.0"; + private static final int MAX_URI_LENGTH = 8000; + private static final String DEFAULT_BID_CURRENCY = "USD"; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public AdoceanBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + if (CollectionUtils.isEmpty(request.getImp())) { + return Result.emptyWithError(BidderError.badInput("No impression in the bid request")); + } + + String consentString = ""; + if (request.getUser() != null && StringUtils.isNotBlank(extUser(request.getUser().getExt()).getConsent())) { + consentString = extUser(request.getUser().getExt()).getConsent(); + } + + final List errors = new ArrayList<>(); + final List> httpRequests = new ArrayList<>(); + for (Imp imp : request.getImp()) { + try { + httpRequests.add(createSingleRequest(httpRequests, request, imp, consentString)); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + return Result.of(Collections.emptyList(), errors); + } + } + + return Result.of(httpRequests, errors); + } + + private ExtUser extUser(ObjectNode extNode) { + try { + return extNode != null ? mapper.mapper().treeToValue(extNode, ExtUser.class) : null; + } catch (JsonProcessingException e) { + throw new PreBidException(e.getMessage(), e); + } + } + + private HttpRequest createSingleRequest(List> httpRequests, BidRequest request, Imp imp, + String consentString) { + final ExtImpAdocean extImpAdocean = parseImpExt(imp); + + if (isRequestAdded(httpRequests, extImpAdocean, imp.getId())) { + throw new PreBidException("Request already exists"); + } + + return HttpRequest.builder() + .method(HttpMethod.GET) + .uri(buildUrl(imp.getId(), extImpAdocean, consentString, request.getTest(), request.getUser())) + .body(null) + .headers(getHeaders(request)) + .payload(null) + .build(); + } + + private ExtImpAdocean parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), ADOCEAN_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage(), e); + } + } + + private boolean isRequestAdded(List> httpRequests, ExtImpAdocean extImpAdocean, String impid) { + for (final HttpRequest request : httpRequests) { + List params = null; + try { + params = URLEncodedUtils.parse(new URI(request.getUri()), StandardCharsets.UTF_8); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + final String masterId = params != null ? params.stream() + .filter(param -> param.getName().equals("id")) + .findFirst() + .map(NameValuePair::getValue) + .orElse(null) : null; + + if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { + final String newSlaveId = params.stream() + .filter(param -> param.getName().equals("aid")) + .map(param -> param.getValue().split(":")[0]) + .filter(slaveId -> slaveId.equals(extImpAdocean.getSlaveId())) + .findFirst() + .orElse(null); + if (StringUtils.isNotBlank(newSlaveId)) { + continue; + } + + params.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); + + final String url = HttpUtil.encodeUrl(String.valueOf(params)); + if (url.length() < MAX_URI_LENGTH) { + request.builder().uri(url); + return true; + } + } + } + return false; + } + + private String buildUrl(String impid, ExtImpAdocean extImpAdocean, String consentString, Integer test, User user) { + final String url = endpointUrl.replace("{{Host}}", extImpAdocean.getEmitterDomain()); + int randomizedPart = 10000000 + (int) (Math.random() * (99999999 - 10000000)); + if (test == 1) { + randomizedPart = 10000000; + } + + final String updateUrl = String.format("%s%s%s%s", url, "/_", randomizedPart, "/ad.json"); + + final List params = new ArrayList<>(); + params.add("pbsrv_v=" + VERSION); + params.add("id=" + extImpAdocean.getMasterId()); + params.add("nc=1"); + params.add("nosecure=1"); + params.add("aid=" + extImpAdocean.getSlaveId() + ":" + impid); + + if (StringUtils.isNotBlank(consentString)) { + params.add("gdpr_consent=" + consentString); + params.add("gdpr=1"); + } + if (user != null && StringUtils.isNotBlank(user.getBuyeruid())) { + params.add("hcuserid=" + user.getBuyeruid()); + } + + final String urlParams = params.stream().sorted().collect(Collectors.joining("&")); + return String.format("%s?%s", updateUrl, urlParams); + } + + private static MultiMap getHeaders(BidRequest request) { + final MultiMap headers = HttpUtil.headers(); + if (request.getDevice() != null) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER.toString(), + request.getDevice().getUa()); + + if (StringUtils.isNotBlank(request.getDevice().getIp())) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, + request.getDevice().getIp()); + } else if (StringUtils.isNotBlank(request.getDevice().getIpv6())) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, + request.getDevice().getIpv6()); + } + } + + if (request.getSite() != null) { + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, request.getSite().getPage()); + } + return headers; + } + + @Override + public Result> makeBids(HttpCall httpCall, BidRequest bidRequest) { + final int statusCode = httpCall.getResponse().getStatusCode(); + if (statusCode == HttpResponseStatus.NO_CONTENT.code()) { + return Result.of(Collections.emptyList(), Collections.emptyList()); + } else if (statusCode == HttpResponseStatus.BAD_REQUEST.code()) { + return Result.emptyWithError(BidderError.badInput("Invalid request.")); + } else if (statusCode != HttpResponseStatus.OK.code()) { + return Result.emptyWithError(BidderError.badServerResponse(String.format("Unexpected HTTP status %s.", + statusCode))); + } + + List params; + try { + params = URLEncodedUtils.parse(new URI(httpCall.getRequest().getUri()), StandardCharsets.UTF_8); + } catch (URISyntaxException e) { + return Result.emptyWithError(BidderError.badInput(e.getMessage())); + } + + final Map auctionIds = params != null ? params.stream() + .filter(param -> param.getName().equals("aid")) + .map(param -> param.getValue().split(":")) + .collect(Collectors.toMap(name -> name[0], value -> value[1])) : null; + List adoceanResponses; + try { + adoceanResponses = getAdoceanResponseAdUnitList(httpCall.getResponse().getBody()); + } catch (PreBidException e) { + return Result.emptyWithError(BidderError + .badServerResponse("Failed to decode: No content to map due to end-of-input")); + } + + final List errors = new ArrayList<>(); + final List bidderBids = new ArrayList<>(); + for (final AdoceanResponseAdUnit adoceanResponse : adoceanResponses) { + if (adoceanResponse.getError().equals("true")) { + continue; + } + + if (auctionIds != null && StringUtils.isNotBlank(auctionIds.get(adoceanResponse.getId()))) { + final BigDecimal price = new BigDecimal(adoceanResponse.getPrice()); + final Integer width = Integer.valueOf(adoceanResponse.getWidth()); + final Integer height = Integer.valueOf(adoceanResponse.getHeight()); + + final Bid updatedBid = Bid.builder() + .id(adoceanResponse.getId()) + .impid(auctionIds.get(adoceanResponse.getId())) + .adm(getAdm(adoceanResponse)) + .price(price) + .w(width) + .h(height) + .crid(adoceanResponse.getCrid()) + .build(); + + final String bidCurrency = adoceanResponse.getCurrency() != null + ? adoceanResponse.getCurrency() + : DEFAULT_BID_CURRENCY; + final BidderBid bidderBid = BidderBid.of(updatedBid, BidType.banner, bidCurrency); + bidderBids.add(bidderBid); + } + } + return Result.of(bidderBids, errors); + } + + private List getAdoceanResponseAdUnitList(String responseBody) { + try { + return mapper.mapper().readValue( + responseBody, + mapper.mapper().getTypeFactory().constructCollectionType(List.class, AdoceanResponseAdUnit.class)); + } catch (IOException ex) { + throw new PreBidException(ex.getMessage()); + } + } + + private String getAdm(AdoceanResponseAdUnit adoceanResponse) { + final StringBuilder measurementCode = new StringBuilder(); + measurementCode.append(""); + + return String.format(measurementCode.toString(), adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) + + HttpUtil.decodeUrl(adoceanResponse.getCode()); + } + + @Override + public Map extractTargeting(ObjectNode ext) { + return Collections.emptyMap(); + } +} diff --git a/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java b/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java new file mode 100644 index 00000000000..9d60de29234 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java @@ -0,0 +1,32 @@ +package org.prebid.server.bidder.adocean.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Value; + +@Builder +@Value +public class AdoceanResponseAdUnit { + + String id; + + String crid; + + String currency; + + String price; + + String width; + + String height; + + String code; + + @JsonProperty("winUrl") + String winUrl; + + @JsonProperty("statsUrl") + String statsUrl; + + String error; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java new file mode 100644 index 00000000000..76fc49d68d6 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java @@ -0,0 +1,22 @@ +package org.prebid.server.proto.openrtb.ext.request.adocean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +/** + * Defines the contract for bidrequest.imp[i].ext.adocean + */ +@AllArgsConstructor(staticName = "of") +@Value +public class ExtImpAdocean { + + @JsonProperty("emiter") + String emitterDomain; + + @JsonProperty("masterId") + String masterId; + + @JsonProperty("slaveId") + String slaveId; +} diff --git a/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java new file mode 100644 index 00000000000..1f9ecf51159 --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java @@ -0,0 +1,53 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +import org.prebid.server.bidder.adocean.AdoceanBidder; +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.BidderInfoCreator; +import org.prebid.server.spring.config.bidder.util.UsersyncerCreator; +import org.prebid.server.spring.env.YamlPropertySourceFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +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 javax.validation.constraints.NotBlank; + +@Configuration +@PropertySource(value = "classpath:/bidder-config/adocean.yaml", factory = YamlPropertySourceFactory.class) +public class AdoceanConfiguration { + + private static final String BIDDER_NAME = "adocean"; + + @Value("${external-url}") + @NotBlank + private String externalUrl; + + @Autowired + private JacksonMapper mapper; + + @Autowired + @Qualifier("adoceanConfigurationProperties") + private BidderConfigurationProperties configProperties; + + @Bean("adoceanConfigurationProperties") + @ConfigurationProperties("adapters.adocean") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps adoceanBidderDeps() { + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(configProperties) + .bidderInfo(BidderInfoCreator.create(configProperties)) + .usersyncerCreator(UsersyncerCreator.create(configProperties.getUsersync(), externalUrl)) + .bidderCreator(() -> new AdoceanBidder(configProperties.getEndpoint(), mapper)) + .assemble(); + } +} diff --git a/src/main/resources/bidder-config/adocean.yaml b/src/main/resources/bidder-config/adocean.yaml new file mode 100644 index 00000000000..146b12edacb --- /dev/null +++ b/src/main/resources/bidder-config/adocean.yaml @@ -0,0 +1,22 @@ +adapters: + adocean: + enabled: false + endpoint: https://{{Host}} + pbs-enforces-gdpr: true + pbs-enforces-ccpa: true + modifying-vast-xml-allowed: true + deprecated-names: + aliases: + meta-info: + maintainer-email: aoteam@gemius.com + app-media-types: + site-media-types: + - banner + supported-vendors: + vendor-id: 328 + usersync: + url: + redirect-url: + cookie-family-name: adocean + type: redirect + support-cors: false diff --git a/src/main/resources/static/bidder-params/adocean.json b/src/main/resources/static/bidder-params/adocean.json new file mode 100644 index 00000000000..7530c64784c --- /dev/null +++ b/src/main/resources/static/bidder-params/adocean.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AdOcean Adapter Params", + "description": "A schema which validates params accepted by the AdOcean adapter", + "type": "object", + "properties": { + "emiter": { + "type": "string", + "description": "AdOcean emiter", + "pattern": ".+" + }, + "masterId": { + "type": "string", + "description": "Master's id", + "pattern": "^[\\w.]+$" + }, + "slaveId": { + "type": "string", + "description": "Slave's id", + "pattern": "^adocean[\\w.]+$" + } + }, + "required": ["emiter", "masterId", "slaveId"] +} diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java new file mode 100644 index 00000000000..a80598c0fe2 --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -0,0 +1,381 @@ +package org.prebid.server.bidder.adocean; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.iab.openrtb.request.Banner; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +import com.iab.openrtb.request.Format; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.User; +import com.iab.openrtb.response.Bid; +import io.netty.handler.codec.http.HttpHeaderValues; +import org.junit.Before; +import org.junit.Test; +import org.prebid.server.VertxTest; +import org.prebid.server.bidder.adocean.model.AdoceanResponseAdUnit; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpCall; +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.bidder.smartrtb.SmartrtbBidder; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.ExtUser; +import org.prebid.server.proto.openrtb.ext.request.adocean.ExtImpAdocean; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.function.Function.identity; +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 java.util.Arrays.asList; + +public class AdoceanBidderTest extends VertxTest { + + private static final String ENDPOINT_URL = "https://{{Host}}"; + + private AdoceanBidder adoceanBidder; + + @Before + public void setUp() { + adoceanBidder = new AdoceanBidder(ENDPOINT_URL, jacksonMapper); + } + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException() + .isThrownBy(() -> new SmartrtbBidder("invalid_url", jacksonMapper)) + .withMessage("URL supplied is not valid: invalid_url"); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfImpressionListSizeIsZero() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(emptyList()) + .build(); + + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .containsOnly(BidderError.badInput("No impression in the bid request")); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder + .id("123") + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1); + assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance"); + } + + @Test + public void makeHttpRequestsShouldSetReturnErrorIfRequestExists() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder() + .ext(mapper.valueToTree(ExtUser.builder() + .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA." + + "IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAA" + + "AFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw").build())) + .build()) + .imp(asList(Imp.builder() + .id("ao-test") + .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) + .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex")))).build(), + Imp.builder() + .id("ao-test") + .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) + .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqis")))).build())) + .test(1) + + .build(); + + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1); + assertThat(result.getErrors().get(0).getMessage()).startsWith("Request already exists"); + } + + @Test + public void makeHttpRequestsShouldSetExpectedRequestUrl() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder() + .ext(mapper.valueToTree(ExtUser.builder() + .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" + + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgA" + + "AAYQEAAAQmAgBC3ZAYzUw").build())) + .build()) + .imp(singletonList(Imp.builder() + .id("ao-test") + .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) + .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex")))).build())) + .test(1) + .build(); + + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getUri) + .containsOnly("https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex:ao-test&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0"); + } + + @Test + public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpIsPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder() + .ext(mapper.valueToTree(ExtUser.builder() + .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" + + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAg" + + "AAAYQEAAAQmAgBC3ZAYzUw").build())) + .build()) + .imp(singletonList(Imp.builder() + .id("ao-test") + .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) + .id("banner_id").build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex")))).build())) + .test(1) + .device(Device.builder().ip("192.168.1.1").build()) + .site(Site.builder().page("http://www.example.com").build()) + .build(); + + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue().get(0).getHeaders()).isNotNull() + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsOnly(tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE), + tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()), + tuple(HttpUtil.X_FORWARDED_FOR_HEADER.toString(), "192.168.1.1"), + tuple(HttpUtil.REFERER_HEADER.toString(), "http://www.example.com")); + } + + @Test + public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpv6IsPresent() { + // given + final BidRequest bidRequest = BidRequest.builder() + .user(User.builder() + .ext(mapper.valueToTree(ExtUser.builder() + .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACA" + + "IAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAA" + + "AYQEAAAQmAgBC3ZAYzUw").build())) + .build()) + .imp(singletonList(Imp.builder() + .id("ao-test") + .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) + .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex")))).build())) + .test(1) + .device(Device.builder().ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334").build()) + .site(Site.builder().page("http://www.example.com").build()) + .build(); + + // when + final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue().get(0).getHeaders()).isNotNull() + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsOnly(tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE), + tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()), + tuple(HttpUtil.X_FORWARDED_FOR_HEADER.toString(), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"), + tuple(HttpUtil.REFERER_HEADER.toString(), "http://www.example.com")); + } + + @Test + public void makeBidsShouldReturnEmptyResultWhenResponseWithNoContent() { + // given + final HttpCall httpCall = HttpCall + .success(null, HttpResponse.of(204, null, null), null); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnEmptyResultWhenResponseStatusIsNotOk() { + // given + final HttpCall httpCall = HttpCall + .success(null, HttpResponse.of(404, null, null), null); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors().get(0).getMessage()).startsWith("Unexpected HTTP status 404."); + assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response); + } + + @Test + public void makeBidsShouldReturnEmptyResultWhenResponseStatusIsBadRequest() { + // given + final HttpCall httpCall = HttpCall + .success(null, HttpResponse.of(400, null, null), null); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors().get(0).getMessage()).startsWith("Invalid request."); + assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input); + } + + @Test + public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { + // given + final HttpCall httpCall = givenHttpCall(null, ""); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(result.getErrors().get(0).getMessage()) + .startsWith("Failed to decode: No content to map due to end-of-input"); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingException { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId") + .build())) + .build(); + + final List adoceanResponseAdUnit = asList(AdoceanResponseAdUnit.builder() + .id("adoceanmyaozpniqismex") + .price("1") + .winUrl("https://win-url.com") + .statsUrl("https://stats-url.com") + .code(" ") + .currency("EUR") + .width("300") + .height("250") + .crid("0af345b42983cc4bc0") + .error("false") + .build(), + AdoceanResponseAdUnit.builder() + .id("adoceanmyaozpniqis") + .price("1") + .winUrl("https://win-url.com") + .statsUrl("https://stats-url.com") + .code(" ") + .currency("EUR") + .width("300") + .height("250") + .crid("0af345b42983cc4bc0") + .error("false") + .build()); + + final HttpCall httpCall = givenHttpCall(null, mapper.writeValueAsString(adoceanResponseAdUnit)); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); + + // then + final StringBuilder admBuilder = new StringBuilder(); + admBuilder.append("") + .append(" "); + final String adm = admBuilder.toString(); + + final BidderBid expected = BidderBid.of( + Bid.builder() + .id("adoceanmyaozpniqismex") + .impid("ao-test") + .adm(adm) + .price(BigDecimal.valueOf(1)) + .crid("0af345b42983cc4bc0") + .w(300) + .h(250) + .build(), + BidType.banner, "EUR"); + assertThat(result.getValue().get(0).getBid().getAdm()).isEqualTo(adm); + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).doesNotContainNull().hasSize(1).element(0).isEqualTo(expected); + } + + private static BidRequest givenBidRequest( + Function bidRequestCustomizer, + Function impCustomizer) { + + return bidRequestCustomizer.apply(BidRequest.builder() + .imp(singletonList(givenImp(impCustomizer)))) + .build(); + } + + private static BidRequest givenBidRequest(Function impCustomizer) { + return givenBidRequest(identity(), impCustomizer); + } + + private static Imp givenImp(Function impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("123") + .banner(Banner.builder().id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex"))))) + .build(); + } + + private static HttpCall givenHttpCall(String requestBody, String responseBody) { + return HttpCall.success( + HttpRequest.builder() + .body(requestBody) + .uri("https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0") + .build(), + HttpResponse.of(200, null, responseBody), null); + } +} diff --git a/src/test/java/org/prebid/server/it/AdoceanTest.java b/src/test/java/org/prebid/server/it/AdoceanTest.java new file mode 100644 index 00000000000..b6ff6b390f8 --- /dev/null +++ b/src/test/java/org/prebid/server/it/AdoceanTest.java @@ -0,0 +1,69 @@ +package org.prebid.server.it; + +import com.github.tomakehurst.wiremock.client.WireMock; +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static io.restassured.RestAssured.given; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class AdoceanTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromAdocean() throws IOException, JSONException { + + WIRE_MOCK_RULE.stubFor(get(urlPathEqualTo("/adocean-exchange")) + .withQueryParam("pbsrv_v", equalTo("1.0.0")) + .withQueryParam("id", equalTo("tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7")) + .withQueryParam("nc", equalTo("1")) + .withQueryParam("nosecure", equalTo("1")) + .withQueryParam("aid", equalTo("adoceanmyaozpniqismex:impId12")) + .withQueryParam("gdpr", equalTo("1")) + .withQueryParam("gdpr_consent", equalTo("consentValue")) + .withQueryParam("hcuserid", equalTo("AO-UID")) + .withHeader("Accept", WireMock.equalTo("application/json")) + .withHeader("Content-Type", WireMock.equalTo("application/json;charset=UTF-8")) + .withHeader("Host", equalTo("localhost:8090")) + .withHeader("X-Forwarded-For", equalTo("193.168.244.1")) + .withHeader("Referer", equalTo("http://www.example.com")) + .withHeader("User-Agent", equalTo("userAgent")) + .withRequestBody(WireMock.equalTo("")) + .willReturn(WireMock.aResponse() + .withBody(jsonFrom("openrtb2/adocean/test-adocean-bid-response-1.json")))); + + // pre-bid cache + WIRE_MOCK_RULE.stubFor(WireMock.post(WireMock.urlPathEqualTo("/cache")) + .withRequestBody(WireMock.equalToJson(jsonFrom("openrtb2/adocean/test-cache-adocean-request.json"))) + .willReturn(WireMock.aResponse() + .withBody(jsonFrom("openrtb2/adocean/test-cache-adocean-response.json")))); + + // when + final Response response = given(SPEC) + .header("Referer", "http://www.example.com") + .header("X-Forwarded-For", "193.168.244.1") + .header("User-Agent", "userAgent") + .header("Origin", "http://www.example.com") + .cookie("uids", "eyJ1aWRzIjp7ImFkb2NlYW4iOiJBTy1VSUQifX0=") + .body(jsonFrom("openrtb2/adocean/test-auction-adocean-request.json")) + .post("/openrtb2/auction"); + + // then + final String expectedAuctionResponse = openrtbAuctionResponseFrom( + "openrtb2/adocean/test-auction-adocean-response.json", + response, singletonList("adocean")); + + String actualStr = response.asString(); + JSONAssert.assertEquals(expectedAuctionResponse, actualStr, JSONCompareMode.NON_EXTENSIBLE); + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json new file mode 100644 index 00000000000..63a78c6fbb6 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json @@ -0,0 +1,14 @@ +[ + { + "id": "adoceanmyaozpniqismex", + "price": "10", + "winurl": "https://win-url.com", + "statsUrl": "https://stats-url.com", + "code": " ", + "currency": "EUR", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "error": "false" + } +] \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json new file mode 100644 index 00000000000..eef8d0cef8e --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json @@ -0,0 +1,95 @@ +{ + "id": "tid", + "imp": [ + { + "id": "impId12", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "adocean": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + } + } + ], + "device": { + "pxratio": 4.2, + "dnt": 2, + "language": "en", + "ifa": "ifaId", + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "site": { + "publisher": { + "id": "publisherId", + "page": "http://www.example.com" + } + }, + "at": 1, + "tmax": 5000, + "cur": [ + "EUR" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "test": 1, + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.2406 + }, + "USD": { + "EUR": 0.8110 + } + } + }, + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + } + }, + "cache": { + "bids": {}, + "vastxml": { + "ttlseconds": 120 + } + }, + "auctiontimestamp": 1000 + } + }, + "user": { + "buyeruid": "AO-UID", + "ext": { + "digitrust": { + "id": "id", + "keyv": 123, + "pref": 0 + }, + "consent": "consentValue" + } + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json new file mode 100644 index 00000000000..b03071f3e87 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -0,0 +1,56 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "id": "adoceanmyaozpniqismex", + "impid": "ao-test", + "price": 1, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner", + "targeting": { + "hb_pb": "10.50", + "hb_size_adocean": "400x300", + "hb_size": "400x300", + "hb_bidder": "adform", + "hb_cache_id_adocean": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce", + "hb_cache_id": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce", + "hb_bidder_adocean": "adocean", + "hb_pb_adocean": "10.50", + "hb_cache_host": "{{ cache.host }}", + "hb_cache_host_adocean": "{{ cache.host }}", + "hb_cache_path": "{{ cache.path }}", + "hb_cache_path_adocean": "{{ cache.path }}" + }, + "cache": { + "bids": { + "url": "{{ cache.resource_url }}ca2a4dd3-f974-4eff-be5c-986bf4e083ce", + "cacheId": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce" + } + } + } + } + } + ], + "seat": "adocean", + "group": 0 + } + ], + "cur": "EUR", + "ext": { + "responsetimemillis": { + "adocean": "{{ adocean.response_time_ms }}", + "cache": "{{ cache.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 1000 + }, + "tmaxrequest": 5000 + } +} \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json new file mode 100644 index 00000000000..ae92156a703 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json @@ -0,0 +1,16 @@ +{ + "puts": [ + { + "type": "json", + "value": { + "id": "impId12", + "impid": "impId12", + "price": 10.5, + "adm": "banner", + "crid": "crid12", + "w": 400, + "h": 300 + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-response.json new file mode 100644 index 00000000000..a1adc7415cc --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-response.json @@ -0,0 +1,7 @@ +{ + "responses": [ + { + "uuid": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 09e15d0d70c..2d5694efa8d 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -10,6 +10,10 @@ adapters.adkernel.enabled=true adapters.adkernel.endpoint=http://%s/adkernel-exchange?zone=%s adapters.adkernel.pbs-enforces-gdpr=true adapters.adkernel.usersync.url=//adkernel-usersync +adapters.adocean.enabled=true +adapters.adocean.endpoint=http://{{Host}} +adapters.adocean.pbs-enforces-gdpr=true +adapters.adocean.usersync.url=//adocean-usersync adapters.adoppler.enabled=true adapters.adoppler.endpoint=http://localhost:8090/adoppler-exchange adapters.adoppler.pbs-enforces-gdpr=true From ecabcef4b6cdaa03ba06f321e71a4315073f7e39 Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Wed, 8 Jul 2020 01:28:45 +0300 Subject: [PATCH 2/9] Add integration test --- .../server/bidder/adocean/AdoceanBidder.java | 25 ++- .../adocean/model/AdoceanResponseAdUnit.java | 2 +- .../bidder/adocean/AdoceanBidderTest.java | 25 ++- .../org/prebid/server/it/AdoceanTest.java | 5 +- .../test-auction-adocean-response.json | 146 ++++++++++++++++-- .../adocean/test-cache-adocean-request.json | 12 +- 6 files changed, 166 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 94080fd77d9..9f879e48416 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -278,19 +278,18 @@ private List getAdoceanResponseAdUnitList(String response private String getAdm(AdoceanResponseAdUnit adoceanResponse) { final StringBuilder measurementCode = new StringBuilder(); - measurementCode.append(""); + measurementCode.append(" "); return String.format(measurementCode.toString(), adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) + HttpUtil.decodeUrl(adoceanResponse.getCode()); diff --git a/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java b/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java index 9d60de29234..b2bd0142cee 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java +++ b/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java @@ -22,7 +22,7 @@ public class AdoceanResponseAdUnit { String code; - @JsonProperty("winUrl") + @JsonProperty("winurl") String winUrl; @JsonProperty("statsUrl") diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index a80598c0fe2..3041f141d4a 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -316,19 +316,18 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio // then final StringBuilder admBuilder = new StringBuilder(); - admBuilder.append("") + admBuilder.append(" ") .append(" "); final String adm = admBuilder.toString(); diff --git a/src/test/java/org/prebid/server/it/AdoceanTest.java b/src/test/java/org/prebid/server/it/AdoceanTest.java index b6ff6b390f8..26a93b698e9 100644 --- a/src/test/java/org/prebid/server/it/AdoceanTest.java +++ b/src/test/java/org/prebid/server/it/AdoceanTest.java @@ -13,7 +13,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static io.restassured.RestAssured.given; import static java.util.Collections.singletonList; @@ -23,7 +22,7 @@ public class AdoceanTest extends IntegrationTest { @Test public void openrtb2AuctionShouldRespondWithBidsFromAdocean() throws IOException, JSONException { - WIRE_MOCK_RULE.stubFor(get(urlPathEqualTo("/adocean-exchange")) + WIRE_MOCK_RULE.stubFor(get(WireMock.urlPathEqualTo("/adocean-exchange/_10000000/ad.json")) .withQueryParam("pbsrv_v", equalTo("1.0.0")) .withQueryParam("id", equalTo("tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7")) .withQueryParam("nc", equalTo("1")) @@ -38,7 +37,7 @@ public void openrtb2AuctionShouldRespondWithBidsFromAdocean() throws IOException .withHeader("X-Forwarded-For", equalTo("193.168.244.1")) .withHeader("Referer", equalTo("http://www.example.com")) .withHeader("User-Agent", equalTo("userAgent")) - .withRequestBody(WireMock.equalTo("")) + .withRequestBody(WireMock.absent()) .willReturn(WireMock.aResponse() .withBody(jsonFrom("openrtb2/adocean/test-adocean-bid-response-1.json")))); diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json index b03071f3e87..6fb480d40a0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -5,8 +5,8 @@ "bid": [ { "id": "adoceanmyaozpniqismex", - "impid": "ao-test", - "price": 1, + "impid": "impId12", + "price": 10, "adm": " ", "crid": "0af345b42983cc4bc0", "w": 300, @@ -15,18 +15,18 @@ "prebid": { "type": "banner", "targeting": { - "hb_pb": "10.50", - "hb_size_adocean": "400x300", - "hb_size": "400x300", - "hb_bidder": "adform", + "hb_pb": "10.00", + "hb_cache_host_adocean": "{{ cache.host }}", + "hb_cache_path": "{{ cache.path }}", + "hb_size": "300x250", + "hb_bidder_adocean": "adocean", + "hb_cache_path_adocean": "{{ cache.path }}", + "hb_bidder": "adocean", "hb_cache_id_adocean": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce", "hb_cache_id": "ca2a4dd3-f974-4eff-be5c-986bf4e083ce", - "hb_bidder_adocean": "adocean", - "hb_pb_adocean": "10.50", + "hb_pb_adocean": "10.00", "hb_cache_host": "{{ cache.host }}", - "hb_cache_host_adocean": "{{ cache.host }}", - "hb_cache_path": "{{ cache.path }}", - "hb_cache_path_adocean": "{{ cache.path }}" + "hb_size_adocean": "300x250" }, "cache": { "bids": { @@ -44,13 +44,133 @@ ], "cur": "EUR", "ext": { + "debug": { + "httpcalls": { + "adocean": [ + { + "uri": "{{ adocean.exchange_uri }}/_10000000/ad.json?aid=adoceanmyaozpniqismex:impId12&gdpr=1&gdpr_consent=consentValue&hcuserid=AO-UID&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0", + "responsebody": "[{\"id\":\"adoceanmyaozpniqismex\",\"price\":\"10\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"},{\"id\":\"adoceanmyaozpniqis\",\"price\":\"12\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"}]", + "status": 200 + } + ], + "cache": [ + { + "uri": "{{ cache.endpoint }}", + "requestbody": "{\"puts\":[{\"type\":\"json\",\"value\":{\"id\":\"adoceanmyaozpniqismex\",\"impid\":\"impId12\",\"price\":10,\"adm\":\" \",\"crid\":\"0af345b42983cc4bc0\",\"w\":300,\"h\":250}}]}", + "responsebody": "{\"responses\":[{\"uuid\":\"ca2a4dd3-f974-4eff-be5c-986bf4e083ce\"}]}", + "status": 200 + } + ] + }, + "resolvedrequest": { + "id": "tid", + "imp": [ + { + "id": "impId12", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "adocean": { + "emiter": "myao.adocean.pl", + "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "slaveId": "adoceanmyaozpniqismex" + } + } + } + ], + "site": { + "domain": "example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "dnt": 2, + "ip": "193.168.244.1", + "pxratio": 4.2, + "language": "en", + "ifa": "ifaId" + }, + "user": { + "buyeruid": "AO-UID", + "ext": { + "digitrust": { + "id": "id", + "keyv": 123, + "pref": 0 + }, + "consent": "consentValue" + } + }, + "test": 1, + "at": 1, + "tmax": 5000, + "cur": [ + "EUR" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "EUR": { + "USD": 1.2406 + }, + "USD": { + "EUR": 0.811 + } + } + }, + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true + }, + "cache": { + "bids": {}, + "vastxml": { + "ttlseconds": 120 + } + }, + "auctiontimestamp": 1000 + } + } + } + }, "responsetimemillis": { "adocean": "{{ adocean.response_time_ms }}", "cache": "{{ cache.response_time_ms }}" }, + "tmaxrequest": 5000, "prebid": { "auctiontimestamp": 1000 - }, - "tmaxrequest": 5000 + } } } \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json index ae92156a703..950f2ead307 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json @@ -3,13 +3,13 @@ { "type": "json", "value": { - "id": "impId12", + "id": "adoceanmyaozpniqismex", "impid": "impId12", - "price": 10.5, - "adm": "banner", - "crid": "crid12", - "w": 400, - "h": 300 + "price": 10, + "adm": " ", + "crid": "0af345b42983cc4bc0", + "w": 300, + "h": 250 } } ] From 2795b1959d50403ea8a4ff1a8dd7d856510743af Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Wed, 8 Jul 2020 01:46:39 +0300 Subject: [PATCH 3/9] Fix types --- .../java/org/prebid/server/bidder/adocean/AdoceanBidder.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 9f879e48416..c71ed437946 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -66,8 +66,9 @@ public Result>> makeHttpRequests(BidRequest request) { } String consentString = ""; - if (request.getUser() != null && StringUtils.isNotBlank(extUser(request.getUser().getExt()).getConsent())) { - consentString = extUser(request.getUser().getExt()).getConsent(); + final User user = request.getUser(); + if (user != null && StringUtils.isNotBlank(extUser(user.getExt()).getConsent())) { + consentString = extUser(user.getExt()).getConsent(); } final List errors = new ArrayList<>(); From 0c2f7edf6679fdcbf44dd63b9f2313b64b3d2e8d Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Wed, 8 Jul 2020 01:52:25 +0300 Subject: [PATCH 4/9] Change extUser method --- .../server/bidder/adocean/AdoceanBidder.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index c71ed437946..45beccbddd3 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -67,8 +67,8 @@ public Result>> makeHttpRequests(BidRequest request) { String consentString = ""; final User user = request.getUser(); - if (user != null && StringUtils.isNotBlank(extUser(user.getExt()).getConsent())) { - consentString = extUser(user.getExt()).getConsent(); + if (user != null && StringUtils.isNotBlank(extUser(user).getConsent())) { + consentString = extUser(user).getConsent(); } final List errors = new ArrayList<>(); @@ -85,12 +85,16 @@ public Result>> makeHttpRequests(BidRequest request) { return Result.of(httpRequests, errors); } - private ExtUser extUser(ObjectNode extNode) { - try { - return extNode != null ? mapper.mapper().treeToValue(extNode, ExtUser.class) : null; - } catch (JsonProcessingException e) { - throw new PreBidException(e.getMessage(), e); + private ExtUser extUser(User user) { + final ObjectNode userExt = user != null ? user.getExt() : null; + if (userExt != null) { + try { + return mapper.mapper().treeToValue(userExt, ExtUser.class); + } catch (JsonProcessingException e) { + throw new PreBidException(String.format("Error decoding bidRequest.user.ext: %s", e.getMessage()), e); + } } + return null; } private HttpRequest createSingleRequest(List> httpRequests, BidRequest request, Imp imp, From f302200ed7ba71c18c38aaf8a4c124fe552157b2 Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Wed, 8 Jul 2020 15:28:47 +0300 Subject: [PATCH 5/9] Change to test json --- .../prebid/server/bidder/adocean/AdoceanBidder.java | 2 +- .../server/bidder/adocean/AdoceanBidderTest.java | 2 +- .../adocean/test-adocean-bid-response-1.json | 12 ++++++++++++ .../adocean/test-auction-adocean-response.json | 2 +- .../org/prebid/server/it/test-application.properties | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 45beccbddd3..208c1545baa 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -294,7 +294,7 @@ private String getAdm(AdoceanResponseAdUnit adoceanResponse) { .append(" (new Image(1,1)).src = su") .append(" }") .append(" }();") - .append(" "); + .append(" "); return String.format(measurementCode.toString(), adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) + HttpUtil.decodeUrl(adoceanResponse.getCode()); diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index 3041f141d4a..859cd346a12 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -327,7 +327,7 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio .append(" (new Image(1,1)).src = su") .append(" }") .append(" }();") - .append(" ") + .append(" ") .append(" "); final String adm = admBuilder.toString(); diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json index 63a78c6fbb6..808472a828b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json @@ -10,5 +10,17 @@ "height": "250", "crid": "0af345b42983cc4bc0", "error": "false" + }, + { + "id": "adoceanmyaozpniqismexs", + "price": "12", + "winurl": "https://win-url.com", + "statsUrl": "https://stats-url.com", + "code": " ", + "currency": "EUR", + "width": "300", + "height": "250", + "crid": "0af345b42983cc4bc0", + "error": "false" } ] \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json index 6fb480d40a0..8d1b8072be0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -49,7 +49,7 @@ "adocean": [ { "uri": "{{ adocean.exchange_uri }}/_10000000/ad.json?aid=adoceanmyaozpniqismex:impId12&gdpr=1&gdpr_consent=consentValue&hcuserid=AO-UID&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0", - "responsebody": "[{\"id\":\"adoceanmyaozpniqismex\",\"price\":\"10\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"},{\"id\":\"adoceanmyaozpniqis\",\"price\":\"12\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"}]", + "responsebody": "[{\"id\":\"adoceanmyaozpniqismex\",\"price\":\"10\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"},{\"id\":\"adoceanmyaozpniqismexs\",\"price\":\"12\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"}]", "status": 200 } ], diff --git a/src/test/resources/org/prebid/server/it/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 3a0c523b1b5..d4581fd78cc 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -11,7 +11,7 @@ adapters.adkernel.endpoint=http://%s/adkernel-exchange?zone=%s adapters.adkernel.pbs-enforces-gdpr=true adapters.adkernel.usersync.url=//adkernel-usersync adapters.adocean.enabled=true -adapters.adocean.endpoint=http://{{Host}} +adapters.adocean.endpoint=http://localhost:8090/adocean-exchange adapters.adocean.pbs-enforces-gdpr=true adapters.adocean.usersync.url=//adocean-usersync adapters.adoppler.enabled=true From 2673bb0c559fb39cbc370ffa5dc18e17416721f0 Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Thu, 9 Jul 2020 00:15:14 +0300 Subject: [PATCH 6/9] Resolve conflicts --- .../server/bidder/adocean/AdoceanBidder.java | 20 +++---------------- .../bidder/adocean/AdoceanBidderTest.java | 16 +++++++-------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 208c1545baa..925d67ce016 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -1,6 +1,5 @@ package org.prebid.server.bidder.adocean; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import com.iab.openrtb.request.BidRequest; @@ -65,11 +64,10 @@ public Result>> makeHttpRequests(BidRequest request) { return Result.emptyWithError(BidderError.badInput("No impression in the bid request")); } - String consentString = ""; final User user = request.getUser(); - if (user != null && StringUtils.isNotBlank(extUser(user).getConsent())) { - consentString = extUser(user).getConsent(); - } + final ExtUser extUser = user != null ? user.getExt() : null; + final String consent = extUser != null ? extUser.getConsent() : null; + final String consentString = StringUtils.isNotBlank(consent) ? consent : ""; final List errors = new ArrayList<>(); final List> httpRequests = new ArrayList<>(); @@ -85,18 +83,6 @@ public Result>> makeHttpRequests(BidRequest request) { return Result.of(httpRequests, errors); } - private ExtUser extUser(User user) { - final ObjectNode userExt = user != null ? user.getExt() : null; - if (userExt != null) { - try { - return mapper.mapper().treeToValue(userExt, ExtUser.class); - } catch (JsonProcessingException e) { - throw new PreBidException(String.format("Error decoding bidRequest.user.ext: %s", e.getMessage()), e); - } - } - return null; - } - private HttpRequest createSingleRequest(List> httpRequests, BidRequest request, Imp imp, String consentString) { final ExtImpAdocean extImpAdocean = parseImpExt(imp); diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index 859cd346a12..2af5e18f579 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -93,10 +93,10 @@ public void makeHttpRequestsShouldSetReturnErrorIfRequestExists() { // given final BidRequest bidRequest = BidRequest.builder() .user(User.builder() - .ext(mapper.valueToTree(ExtUser.builder() + .ext(ExtUser.builder() .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA." + "IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAA" - + "AFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw").build())) + + "AFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw").build()) .build()) .imp(asList(Imp.builder() .id("ao-test") @@ -127,10 +127,10 @@ public void makeHttpRequestsShouldSetExpectedRequestUrl() { // given final BidRequest bidRequest = BidRequest.builder() .user(User.builder() - .ext(mapper.valueToTree(ExtUser.builder() + .ext(ExtUser.builder() .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgA" - + "AAYQEAAAQmAgBC3ZAYzUw").build())) + + "AAYQEAAAQmAgBC3ZAYzUw").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") @@ -156,10 +156,10 @@ public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpIsPresent() { // given final BidRequest bidRequest = BidRequest.builder() .user(User.builder() - .ext(mapper.valueToTree(ExtUser.builder() + .ext(ExtUser.builder() .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAg" - + "AAAYQEAAAQmAgBC3ZAYzUw").build())) + + "AAAYQEAAAQmAgBC3ZAYzUw").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") @@ -190,10 +190,10 @@ public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpv6IsPresent() { // given final BidRequest bidRequest = BidRequest.builder() .user(User.builder() - .ext(mapper.valueToTree(ExtUser.builder() + .ext(ExtUser.builder() .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACA" + "IAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAA" - + "AYQEAAAQmAgBC3ZAYzUw").build())) + + "AYQEAAAQmAgBC3ZAYzUw").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") From 6248def17bb8c54cff52580baf0e43371a0181fa Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Tue, 18 Aug 2020 19:29:32 +0300 Subject: [PATCH 7/9] Refactoring after review --- .../server/bidder/adocean/AdoceanBidder.java | 155 ++++++++------- .../bidder/adocean/AdoceanBidderTest.java | 176 +++++++++++------- .../test-auction-adocean-response.json | 6 +- .../adocean/test-cache-adocean-request.json | 2 +- 4 files changed, 183 insertions(+), 156 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 925d67ce016..17f53a59e92 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -12,6 +12,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; import org.prebid.server.bidder.Bidder; @@ -49,6 +50,20 @@ public class AdoceanBidder implements Bidder { private static final String VERSION = "1.0.0"; private static final int MAX_URI_LENGTH = 8000; private static final String DEFAULT_BID_CURRENCY = "USD"; + private static final String MEASUREMENT_CODE = ""; private final String endpointUrl; private final JacksonMapper mapper; @@ -73,7 +88,11 @@ public Result>> makeHttpRequests(BidRequest request) { final List> httpRequests = new ArrayList<>(); for (Imp imp : request.getImp()) { try { - httpRequests.add(createSingleRequest(httpRequests, request, imp, consentString)); + final ExtImpAdocean extImpAdocean = parseImpExt(imp); + if (isRequest(httpRequests, extImpAdocean, imp.getId())) { + continue; + } + httpRequests.add(createSingleRequest(request, imp, extImpAdocean, consentString)); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); return Result.of(Collections.emptyList(), errors); @@ -83,20 +102,13 @@ public Result>> makeHttpRequests(BidRequest request) { return Result.of(httpRequests, errors); } - private HttpRequest createSingleRequest(List> httpRequests, BidRequest request, Imp imp, + private HttpRequest createSingleRequest(BidRequest request, Imp imp, ExtImpAdocean extImpAdocean, String consentString) { - final ExtImpAdocean extImpAdocean = parseImpExt(imp); - - if (isRequestAdded(httpRequests, extImpAdocean, imp.getId())) { - throw new PreBidException("Request already exists"); - } return HttpRequest.builder() .method(HttpMethod.GET) .uri(buildUrl(imp.getId(), extImpAdocean, consentString, request.getTest(), request.getUser())) - .body(null) .headers(getHeaders(request)) - .payload(null) .build(); } @@ -108,22 +120,27 @@ private ExtImpAdocean parseImpExt(Imp imp) { } } - private boolean isRequestAdded(List> httpRequests, ExtImpAdocean extImpAdocean, String impid) { - for (final HttpRequest request : httpRequests) { + private boolean isRequest(List> httpRequests, ExtImpAdocean extImpAdocean, String impid) { + for (HttpRequest request : httpRequests) { List params = null; + URIBuilder uriBuilder = null; try { - params = URLEncodedUtils.parse(new URI(request.getUri()), StandardCharsets.UTF_8); + uriBuilder = new URIBuilder(request.getUri()); } catch (URISyntaxException e) { e.printStackTrace(); } - final String masterId = params != null ? params.stream() + final List queryParams = uriBuilder != null ? uriBuilder.getQueryParams() : null; + + final String masterId = queryParams != null + ? queryParams.stream() .filter(param -> param.getName().equals("id")) .findFirst() .map(NameValuePair::getValue) - .orElse(null) : null; + .orElse(null) + : null; if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { - final String newSlaveId = params.stream() + final String newSlaveId = queryParams.stream() .filter(param -> param.getName().equals("aid")) .map(param -> param.getValue().split(":")[0]) .filter(slaveId -> slaveId.equals(extImpAdocean.getSlaveId())) @@ -133,13 +150,11 @@ private boolean isRequestAdded(List> httpRequests, ExtImpAdoce continue; } - params.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); - final String url = HttpUtil.encodeUrl(String.valueOf(params)); if (url.length() < MAX_URI_LENGTH) { - request.builder().uri(url); return true; } + queryParams.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); } } return false; @@ -148,29 +163,29 @@ private boolean isRequestAdded(List> httpRequests, ExtImpAdoce private String buildUrl(String impid, ExtImpAdocean extImpAdocean, String consentString, Integer test, User user) { final String url = endpointUrl.replace("{{Host}}", extImpAdocean.getEmitterDomain()); int randomizedPart = 10000000 + (int) (Math.random() * (99999999 - 10000000)); - if (test == 1) { + if (test != null && test == 1) { randomizedPart = 10000000; } - final String updateUrl = String.format("%s%s%s%s", url, "/_", randomizedPart, "/ad.json"); - - final List params = new ArrayList<>(); - params.add("pbsrv_v=" + VERSION); - params.add("id=" + extImpAdocean.getMasterId()); - params.add("nc=1"); - params.add("nosecure=1"); - params.add("aid=" + extImpAdocean.getSlaveId() + ":" + impid); + final String updateUrl = String.format("%s/_%s/ad.json", url, randomizedPart); + final URIBuilder uriBuilder = new URIBuilder() + .setPath(updateUrl) + .addParameter("pbsrv_v", VERSION) + .addParameter("id", extImpAdocean.getMasterId()) + .addParameter("nc", "1") + .addParameter("nosecure", "1") + .addParameter("aid", extImpAdocean.getSlaveId() + ":" + impid); if (StringUtils.isNotBlank(consentString)) { - params.add("gdpr_consent=" + consentString); - params.add("gdpr=1"); + uriBuilder.addParameter("gdpr_consent", consentString); + uriBuilder.addParameter("gdpr", "1"); } + if (user != null && StringUtils.isNotBlank(user.getBuyeruid())) { - params.add("hcuserid=" + user.getBuyeruid()); + uriBuilder.addParameter("hcuserid", user.getBuyeruid()); } - final String urlParams = params.stream().sorted().collect(Collectors.joining("&")); - return String.format("%s?%s", updateUrl, urlParams); + return uriBuilder.toString(); } private static MultiMap getHeaders(BidRequest request) { @@ -225,36 +240,35 @@ public Result> makeBids(HttpCall httpCall, BidRequest bidR .badServerResponse("Failed to decode: No content to map due to end-of-input")); } - final List errors = new ArrayList<>(); - final List bidderBids = new ArrayList<>(); - for (final AdoceanResponseAdUnit adoceanResponse : adoceanResponses) { - if (adoceanResponse.getError().equals("true")) { - continue; - } + final List bidderBids = adoceanResponses.stream() + .filter(adoceanResponse -> !adoceanResponse.getError().equals("true")) + .filter(adoceanResponse -> auctionIds != null + && StringUtils.isNotBlank(auctionIds.get(adoceanResponse.getId()))) + .map(adoceanResponse -> BidderBid.of(createBid(auctionIds, adoceanResponse), BidType.banner, + getBidCurrency(adoceanResponse))) + .collect(Collectors.toList()); - if (auctionIds != null && StringUtils.isNotBlank(auctionIds.get(adoceanResponse.getId()))) { - final BigDecimal price = new BigDecimal(adoceanResponse.getPrice()); - final Integer width = Integer.valueOf(adoceanResponse.getWidth()); - final Integer height = Integer.valueOf(adoceanResponse.getHeight()); - - final Bid updatedBid = Bid.builder() - .id(adoceanResponse.getId()) - .impid(auctionIds.get(adoceanResponse.getId())) - .adm(getAdm(adoceanResponse)) - .price(price) - .w(width) - .h(height) - .crid(adoceanResponse.getCrid()) - .build(); - - final String bidCurrency = adoceanResponse.getCurrency() != null - ? adoceanResponse.getCurrency() - : DEFAULT_BID_CURRENCY; - final BidderBid bidderBid = BidderBid.of(updatedBid, BidType.banner, bidCurrency); - bidderBids.add(bidderBid); - } - } - return Result.of(bidderBids, errors); + return Result.of(bidderBids, Collections.emptyList()); + } + + private static Bid createBid(Map auctionIds, AdoceanResponseAdUnit adoceanResponse) { + final String adm = String.format(MEASUREMENT_CODE, adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) + + HttpUtil.decodeUrl(adoceanResponse.getCode()); + return Bid.builder() + .id(adoceanResponse.getId()) + .impid(auctionIds.get(adoceanResponse.getId())) + .adm(adm) + .price(new BigDecimal(adoceanResponse.getPrice())) + .w(Integer.valueOf(adoceanResponse.getWidth())) + .h(Integer.valueOf(adoceanResponse.getHeight())) + .crid(adoceanResponse.getCrid()) + .build(); + } + + private static String getBidCurrency(AdoceanResponseAdUnit adoceanResponse) { + return adoceanResponse.getCurrency() != null + ? adoceanResponse.getCurrency() + : DEFAULT_BID_CURRENCY; } private List getAdoceanResponseAdUnitList(String responseBody) { @@ -267,25 +281,6 @@ private List getAdoceanResponseAdUnitList(String response } } - private String getAdm(AdoceanResponseAdUnit adoceanResponse) { - final StringBuilder measurementCode = new StringBuilder(); - measurementCode.append(" "); - - return String.format(measurementCode.toString(), adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) - + HttpUtil.decodeUrl(adoceanResponse.getCode()); - } - @Override public Map extractTargeting(ObjectNode ext) { return Collections.emptyMap(); diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index 2af5e18f579..44172b58cdd 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -28,6 +28,7 @@ import org.prebid.server.util.HttpUtil; import java.math.BigDecimal; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -88,56 +89,19 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { assertThat(result.getErrors().get(0).getMessage()).startsWith("Cannot deserialize instance"); } - @Test - public void makeHttpRequestsShouldSetReturnErrorIfRequestExists() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA." - + "IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAA" - + "AFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw").build()) - .build()) - .imp(asList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) - .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))).build(), - Imp.builder() - .id("ao-test") - .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) - .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqis")))).build())) - .test(1) - - .build(); - - // when - final Result>> result = adoceanBidder.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).hasSize(1); - assertThat(result.getErrors().get(0).getMessage()).startsWith("Request already exists"); - } - @Test public void makeHttpRequestsShouldSetExpectedRequestUrl() { // given final BidRequest bidRequest = BidRequest.builder() .user(User.builder() .ext(ExtUser.builder() - .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" - + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgA" - + "AAYQEAAAQmAgBC3ZAYzUw").build()) + .consent("consent").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") - .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) - .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))).build())) + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpAdocean.of("myao.adocean.pl", "masterId", + "slaveId")))).build())) .test(1) .build(); @@ -148,7 +112,8 @@ public void makeHttpRequestsShouldSetExpectedRequestUrl() { assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).hasSize(1) .extracting(HttpRequest::getUri) - .containsOnly("https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex:ao-test&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0"); + .containsOnly("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.0.0&id=masterId&nc=1&nosecure=1" + + "&aid=slaveId%3Aao-test&gdpr_consent=consent&gdpr=1"); } @Test @@ -157,17 +122,17 @@ public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpIsPresent() { final BidRequest bidRequest = BidRequest.builder() .user(User.builder() .ext(ExtUser.builder() - .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAAC" - + "AIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAg" - + "AAAYQEAAAQmAgBC3ZAYzUw").build()) + .consent("consent").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) .id("banner_id").build()) .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))).build())) + ExtImpAdocean.of("myao.adocean.pl", + "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", + "adoceanmyaozpniqismex")))) + .build())) .test(1) .device(Device.builder().ip("192.168.1.1").build()) .site(Site.builder().page("http://www.example.com").build()) @@ -191,16 +156,14 @@ public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpv6IsPresent() { final BidRequest bidRequest = BidRequest.builder() .user(User.builder() .ext(ExtUser.builder() - .consent("COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACA" - + "IAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAA" - + "AYQEAAAQmAgBC3ZAYzUw").build()) + .consent("consent").build()) .build()) .imp(singletonList(Imp.builder() .id("ao-test") - .banner(Banner.builder().format(singletonList(Format.builder().w(300).h(250).build())) - .id("banner_id").build()).ext(mapper.valueToTree(ExtPrebid.of(null, + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))).build())) + "adoceanmyaozpniqismex")))) + .build())) .test(1) .device(Device.builder().ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334").build()) .site(Site.builder().page("http://www.example.com").build()) @@ -283,9 +246,8 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio .id("impId") .build())) .build(); - final List adoceanResponseAdUnit = asList(AdoceanResponseAdUnit.builder() - .id("adoceanmyaozpniqismex") + .id("ad") .price("1") .winUrl("https://win-url.com") .statsUrl("https://stats-url.com") @@ -315,25 +277,24 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); // then - final StringBuilder admBuilder = new StringBuilder(); - admBuilder.append(" ") - .append(" "); - final String adm = admBuilder.toString(); + final String adm = " "; final BidderBid expected = BidderBid.of( Bid.builder() - .id("adoceanmyaozpniqismex") + .id("ad") .impid("ao-test") .adm(adm) .price(BigDecimal.valueOf(1)) @@ -347,6 +308,76 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio assertThat(result.getValue()).doesNotContainNull().hasSize(1).element(0).isEqualTo(expected); } + @Test + public void makeBidsShouldReturnEmptyListOfBids() throws JsonProcessingException { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(singletonList(Imp.builder() + .id("impId") + .build())) + .build(); + final List adoceanResponseAdUnit = asList(AdoceanResponseAdUnit.builder() + .id("ad") + .price("1") + .winUrl("https://win-url.com") + .statsUrl("https://stats-url.com") + .code(" ") + .currency("EUR") + .width("300") + .height("250") + .crid("0af345b42983cc4bc0") + .error("true") + .build(), + AdoceanResponseAdUnit.builder() + .id("adoceanmyaozpniqis") + .price("1") + .winUrl("https://win-url.com") + .statsUrl("https://stats-url.com") + .code(" ") + .currency("EUR") + .width("300") + .height("250") + .crid("0af345b42983cc4bc0") + .error("false") + .build()); + + final HttpCall httpCall = givenHttpCall(null, mapper.writeValueAsString(adoceanResponseAdUnit)); + + // when + final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); + + // then + final String adm = " "; + + final BidderBid expected = BidderBid.of( + Bid.builder() + .id("ad") + .impid("ao-test") + .adm(adm) + .price(BigDecimal.valueOf(1)) + .crid("0af345b42983cc4bc0") + .w(300) + .h(250) + .build(), + BidType.banner, "EUR"); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEqualTo(Collections.emptyList()); + } + private static BidRequest givenBidRequest( Function bidRequestCustomizer, Function impCustomizer) { @@ -373,7 +404,8 @@ private static HttpCall givenHttpCall(String requestBody, String responseB return HttpCall.success( HttpRequest.builder() .body(requestBody) - .uri("https://myao.adocean.pl/_10000000/ad.json?aid=adoceanmyaozpniqismex%3Aao-test&gdpr=1&gdpr_consent=COwK6gaOwK6gaFmAAAENAPCAAAAAAAAAAAAAAAAAAAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0") + .uri("https://myao.adocean.pl/_10000000/ad.json?aid=ad%3Aao-test&gdpr=1&gdpr_consent=consent" + + "&nc=1&nosecure=1&pbsrv_v=1.0.0") .build(), HttpResponse.of(200, null, responseBody), null); } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json index 8d1b8072be0..f2cc8a59810 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -7,7 +7,7 @@ "id": "adoceanmyaozpniqismex", "impid": "impId12", "price": 10, - "adm": " ", + "adm": " ", "crid": "0af345b42983cc4bc0", "w": 300, "h": 250, @@ -48,7 +48,7 @@ "httpcalls": { "adocean": [ { - "uri": "{{ adocean.exchange_uri }}/_10000000/ad.json?aid=adoceanmyaozpniqismex:impId12&gdpr=1&gdpr_consent=consentValue&hcuserid=AO-UID&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&pbsrv_v=1.0.0", + "uri": "{{ adocean.exchange_uri }}/_10000000/ad.json?pbsrv_v=1.0.0&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1&aid=adoceanmyaozpniqismex%3AimpId12&gdpr_consent=consentValue&gdpr=1&hcuserid=AO-UID", "responsebody": "[{\"id\":\"adoceanmyaozpniqismex\",\"price\":\"10\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"},{\"id\":\"adoceanmyaozpniqismexs\",\"price\":\"12\",\"winurl\":\"https://win-url.com\",\"statsUrl\":\"https://stats-url.com\",\"code\":\" \",\"currency\":\"EUR\",\"width\":\"300\",\"height\":\"250\",\"crid\":\"0af345b42983cc4bc0\",\"error\":\"false\"}]", "status": 200 } @@ -56,7 +56,7 @@ "cache": [ { "uri": "{{ cache.endpoint }}", - "requestbody": "{\"puts\":[{\"type\":\"json\",\"value\":{\"id\":\"adoceanmyaozpniqismex\",\"impid\":\"impId12\",\"price\":10,\"adm\":\" \",\"crid\":\"0af345b42983cc4bc0\",\"w\":300,\"h\":250}}]}", + "requestbody": "{\"puts\":[{\"type\":\"json\",\"value\":{\"id\":\"adoceanmyaozpniqismex\",\"impid\":\"impId12\",\"price\":10,\"adm\":\" \",\"crid\":\"0af345b42983cc4bc0\",\"w\":300,\"h\":250}}]}", "responsebody": "{\"responses\":[{\"uuid\":\"ca2a4dd3-f974-4eff-be5c-986bf4e083ce\"}]}", "status": 200 } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json index 950f2ead307..56341f8814b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json @@ -6,7 +6,7 @@ "id": "adoceanmyaozpniqismex", "impid": "impId12", "price": 10, - "adm": " ", + "adm": " ", "crid": "0af345b42983cc4bc0", "w": 300, "h": 250 From 8ecf74d3dedfc88706bf24439de72c3195382f6a Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Thu, 20 Aug 2020 14:05:40 +0300 Subject: [PATCH 8/9] Fix string constant and remove errors --- .../server/bidder/adocean/AdoceanBidder.java | 112 ++++++++---------- .../bidder/adocean/AdoceanBidderTest.java | 28 +---- .../test-auction-adocean-response.json | 4 +- .../adocean/test-cache-adocean-request.json | 2 +- 4 files changed, 53 insertions(+), 93 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 17f53a59e92..9661fdde434 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -50,20 +50,10 @@ public class AdoceanBidder implements Bidder { private static final String VERSION = "1.0.0"; private static final int MAX_URI_LENGTH = 8000; private static final String DEFAULT_BID_CURRENCY = "USD"; - private static final String MEASUREMENT_CODE = ""; + private static final String MEASUREMENT_CODE = " "; private final String endpointUrl; private final JacksonMapper mapper; @@ -84,32 +74,20 @@ public Result>> makeHttpRequests(BidRequest request) { final String consent = extUser != null ? extUser.getConsent() : null; final String consentString = StringUtils.isNotBlank(consent) ? consent : ""; - final List errors = new ArrayList<>(); final List> httpRequests = new ArrayList<>(); for (Imp imp : request.getImp()) { try { final ExtImpAdocean extImpAdocean = parseImpExt(imp); - if (isRequest(httpRequests, extImpAdocean, imp.getId())) { + if (addRequestAndCheckIfDuplicates(httpRequests, extImpAdocean, imp.getId())) { continue; } httpRequests.add(createSingleRequest(request, imp, extImpAdocean, consentString)); } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - return Result.of(Collections.emptyList(), errors); + return Result.emptyWithError(BidderError.badInput(e.getMessage())); } } - return Result.of(httpRequests, errors); - } - - private HttpRequest createSingleRequest(BidRequest request, Imp imp, ExtImpAdocean extImpAdocean, - String consentString) { - - return HttpRequest.builder() - .method(HttpMethod.GET) - .uri(buildUrl(imp.getId(), extImpAdocean, consentString, request.getTest(), request.getUser())) - .headers(getHeaders(request)) - .build(); + return Result.of(httpRequests, Collections.emptyList()); } private ExtImpAdocean parseImpExt(Imp imp) { @@ -120,53 +98,60 @@ private ExtImpAdocean parseImpExt(Imp imp) { } } - private boolean isRequest(List> httpRequests, ExtImpAdocean extImpAdocean, String impid) { + private boolean addRequestAndCheckIfDuplicates(List> httpRequests, ExtImpAdocean extImpAdocean, + String impid) { for (HttpRequest request : httpRequests) { List params = null; - URIBuilder uriBuilder = null; try { - uriBuilder = new URIBuilder(request.getUri()); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - final List queryParams = uriBuilder != null ? uriBuilder.getQueryParams() : null; - - final String masterId = queryParams != null - ? queryParams.stream() - .filter(param -> param.getName().equals("id")) - .findFirst() - .map(NameValuePair::getValue) - .orElse(null) - : null; - - if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { - final String newSlaveId = queryParams.stream() - .filter(param -> param.getName().equals("aid")) - .map(param -> param.getValue().split(":")[0]) - .filter(slaveId -> slaveId.equals(extImpAdocean.getSlaveId())) + URIBuilder uriBuilder = new URIBuilder(request.getUri()); + final List queryParams = uriBuilder.getQueryParams(); + + final String masterId = queryParams != null + ? queryParams.stream() + .filter(param -> param.getName().equals("id")) .findFirst() - .orElse(null); - if (StringUtils.isNotBlank(newSlaveId)) { - continue; + .map(NameValuePair::getValue) + .orElse(null) + : null; + + if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { + final String newSlaveId = queryParams.stream() + .filter(param -> param.getName().equals("aid")) + .map(param -> param.getValue().split(":")[0]) + .filter(slaveId -> slaveId.equals(extImpAdocean.getSlaveId())) + .findFirst() + .orElse(null); + if (StringUtils.isNotBlank(newSlaveId)) { + continue; + } + + final String url = HttpUtil.encodeUrl(String.valueOf(params)); + if (url.length() < MAX_URI_LENGTH) { + return true; + } + queryParams.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); } - final String url = HttpUtil.encodeUrl(String.valueOf(params)); - if (url.length() < MAX_URI_LENGTH) { - return true; - } - queryParams.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); + } catch (URISyntaxException e) { + throw new PreBidException(e.getMessage()); } } return false; } + private HttpRequest createSingleRequest(BidRequest request, Imp imp, ExtImpAdocean extImpAdocean, + String consentString) { + + return HttpRequest.builder() + .method(HttpMethod.GET) + .uri(buildUrl(imp.getId(), extImpAdocean, consentString, request.getTest(), request.getUser())) + .headers(getHeaders(request)) + .build(); + } + private String buildUrl(String impid, ExtImpAdocean extImpAdocean, String consentString, Integer test, User user) { final String url = endpointUrl.replace("{{Host}}", extImpAdocean.getEmitterDomain()); - int randomizedPart = 10000000 + (int) (Math.random() * (99999999 - 10000000)); - if (test != null && test == 1) { - randomizedPart = 10000000; - } - + final int randomizedPart = test != null && test == 1 ? 10000000 : 10000000 + (int) (Math.random() * 89999999); final String updateUrl = String.format("%s/_%s/ad.json", url, randomizedPart); final URIBuilder uriBuilder = new URIBuilder() .setPath(updateUrl) @@ -232,6 +217,7 @@ public Result> makeBids(HttpCall httpCall, BidRequest bidR .filter(param -> param.getName().equals("aid")) .map(param -> param.getValue().split(":")) .collect(Collectors.toMap(name -> name[0], value -> value[1])) : null; + List adoceanResponses; try { adoceanResponses = getAdoceanResponseAdUnitList(httpCall.getResponse().getBody()); diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index 44172b58cdd..712ee9d7009 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -277,20 +277,7 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); // then - final String adm = " "; + final String adm = " "; final BidderBid expected = BidderBid.of( Bid.builder() @@ -361,19 +348,6 @@ public void makeBidsShouldReturnEmptyListOfBids() throws JsonProcessingException + "\t\t\t}\n" + "\t\t}();\n" + "\t "; - - final BidderBid expected = BidderBid.of( - Bid.builder() - .id("ad") - .impid("ao-test") - .adm(adm) - .price(BigDecimal.valueOf(1)) - .crid("0af345b42983cc4bc0") - .w(300) - .h(250) - .build(), - BidType.banner, "EUR"); - assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).isEqualTo(Collections.emptyList()); } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json index f2cc8a59810..dbb266def70 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json @@ -7,7 +7,7 @@ "id": "adoceanmyaozpniqismex", "impid": "impId12", "price": 10, - "adm": " ", + "adm": " ", "crid": "0af345b42983cc4bc0", "w": 300, "h": 250, @@ -56,7 +56,7 @@ "cache": [ { "uri": "{{ cache.endpoint }}", - "requestbody": "{\"puts\":[{\"type\":\"json\",\"value\":{\"id\":\"adoceanmyaozpniqismex\",\"impid\":\"impId12\",\"price\":10,\"adm\":\" \",\"crid\":\"0af345b42983cc4bc0\",\"w\":300,\"h\":250}}]}", + "requestbody": "{\"puts\":[{\"type\":\"json\",\"value\":{\"id\":\"adoceanmyaozpniqismex\",\"impid\":\"impId12\",\"price\":10,\"adm\":\" \",\"crid\":\"0af345b42983cc4bc0\",\"w\":300,\"h\":250}}]}", "responsebody": "{\"responses\":[{\"uuid\":\"ca2a4dd3-f974-4eff-be5c-986bf4e083ce\"}]}", "status": 200 } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json index 56341f8814b..950f2ead307 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-cache-adocean-request.json @@ -6,7 +6,7 @@ "id": "adoceanmyaozpniqismex", "impid": "impId12", "price": 10, - "adm": " ", + "adm": " ", "crid": "0af345b42983cc4bc0", "w": 300, "h": 250 From 13d2d47faace1950647d3191db6ac9ecd4302acb Mon Sep 17 00:00:00 2001 From: apavlyuk Date: Thu, 20 Aug 2020 16:01:10 +0300 Subject: [PATCH 9/9] Small refactoring string constant --- .../server/bidder/adocean/AdoceanBidder.java | 20 +++++++++--------- .../bidder/adocean/AdoceanBidderTest.java | 21 ++++++------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java index 9661fdde434..f547a0fc237 100644 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java @@ -50,10 +50,12 @@ public class AdoceanBidder implements Bidder { private static final String VERSION = "1.0.0"; private static final int MAX_URI_LENGTH = 8000; private static final String DEFAULT_BID_CURRENCY = "USD"; - private static final String MEASUREMENT_CODE = " "; + private static final String MEASUREMENT_CODE_TEMPLATE = " "; private final String endpointUrl; private final JacksonMapper mapper; @@ -106,13 +108,11 @@ private boolean addRequestAndCheckIfDuplicates(List> httpReque URIBuilder uriBuilder = new URIBuilder(request.getUri()); final List queryParams = uriBuilder.getQueryParams(); - final String masterId = queryParams != null - ? queryParams.stream() + final String masterId = queryParams.stream() .filter(param -> param.getName().equals("id")) .findFirst() .map(NameValuePair::getValue) - .orElse(null) - : null; + .orElse(null); if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { final String newSlaveId = queryParams.stream() @@ -238,8 +238,8 @@ public Result> makeBids(HttpCall httpCall, BidRequest bidR } private static Bid createBid(Map auctionIds, AdoceanResponseAdUnit adoceanResponse) { - final String adm = String.format(MEASUREMENT_CODE, adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) - + HttpUtil.decodeUrl(adoceanResponse.getCode()); + final String adm = String.format(MEASUREMENT_CODE_TEMPLATE, adoceanResponse.getWinUrl(), + adoceanResponse.getStatsUrl()) + HttpUtil.decodeUrl(adoceanResponse.getCode()); return Bid.builder() .id(adoceanResponse.getId()) .impid(auctionIds.get(adoceanResponse.getId())) diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java index 712ee9d7009..c584b743f89 100644 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java @@ -277,7 +277,12 @@ public void makeBidsShouldReturnCorrectBidderBid() throws JsonProcessingExceptio final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); // then - final String adm = " "; + final String adm = " "; final BidderBid expected = BidderBid.of( Bid.builder() @@ -334,20 +339,6 @@ public void makeBidsShouldReturnEmptyListOfBids() throws JsonProcessingException final Result> result = adoceanBidder.makeBids(httpCall, bidRequest); // then - final String adm = " "; assertThat(result.getErrors()).isEmpty(); assertThat(result.getValue()).isEqualTo(Collections.emptyList()); }