diff --git a/src/main/java/org/prebid/server/bidder/synapsehx/SynapseHXBidder.java b/src/main/java/org/prebid/server/bidder/synapsehx/SynapseHXBidder.java new file mode 100644 index 00000000000..2e18c977e30 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/synapsehx/SynapseHXBidder.java @@ -0,0 +1,175 @@ +package org.prebid.server.bidder.synapsehx; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.vertx.core.MultiMap; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.http.client.utils.URIBuilder; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderCall; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.synapsehx.ExtImpSynapseHX; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid; +import org.prebid.server.util.BidderUtil; +import org.prebid.server.util.HttpUtil; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class SynapseHXBidder implements Bidder { + + private static final TypeReference> SYNAPSE_HX_EXT_TYPE_REFERENCE = + new TypeReference<>() { }; + + private static final TypeReference> EXT_PREBID_TYPE_REFERENCE = + new TypeReference<>() { }; + + private static final String OPENRTB_VERSION = "2.6"; + + private final String endpoint; + private final JacksonMapper mapper; + + public SynapseHXBidder(String endpoint, JacksonMapper mapper) { + this.endpoint = HttpUtil.validateUrl(Objects.requireNonNull(endpoint)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public final Result>> makeHttpRequests(BidRequest bidRequest) { + final List errors = new ArrayList<>(); + + final BidRequest filteredBidRequest = bidRequest.toBuilder() + .imp(bidRequest.getImp() + .stream() + .map(imp -> validateImp(imp, errors)) + .filter(Objects::nonNull) + .toList()) + .build(); + + if (filteredBidRequest.getImp().isEmpty()) { + return Result.withErrors(errors); + } + + final Imp firstImp = filteredBidRequest.getImp().getFirst(); + final String tenantId; + + try { + tenantId = mapper.mapper() + .convertValue(firstImp.getExt(), SYNAPSE_HX_EXT_TYPE_REFERENCE) + .getBidder() + .getTenantId(); + } catch (IllegalArgumentException e) { + return Result.withError(BidderError.badInput("Failed to parse bidder parameters")); + } + + final MultiMap headers = HttpUtil.headers(); + + headers.add(HttpUtil.X_OPENRTB_VERSION_HEADER, OPENRTB_VERSION); + + final URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(endpoint); + } catch (URISyntaxException e) { + return Result.withError(BidderError.badInput("Invalid endpoint URI")); + } + + return Result.of(List.of(BidderUtil.defaultRequest(filteredBidRequest, + headers, uriBuilder.addParameter("pid", tenantId).toString(), mapper)), errors); + } + + private static Imp validateImp(Imp imp, List errors) { + if (imp.getBanner() == null && imp.getVideo() == null) { + errors.add(BidderError.badInput( + "imp[%s]: Unsupported media type, bidder supports only banner and video".formatted(imp.getId()))); + return null; + } + if (imp.getXNative() != null || imp.getAudio() != null) { + return imp.toBuilder().xNative(null).audio(null).build(); + } + return imp; + } + + @Override + public final Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + try { + final List bidderErrors = new ArrayList<>(); + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.of(extractBids(bidResponse, bidderErrors), bidderErrors); + } catch (DecodeException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidResponse bidResponse, List bidderErrors) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + return bidsFromResponse(bidResponse, bidderErrors); + } + + private List bidsFromResponse(BidResponse bidResponse, List bidderErrors) { + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(bid -> makeBidderBid(bid, bidResponse, bidderErrors)) + .filter(Objects::nonNull) + .toList(); + } + + private BidderBid makeBidderBid(Bid bid, BidResponse bidResponse, List bidderErrors) { + final BidType bidType = getBidType(bid, bidderErrors); + if (bidType == null) { + return null; + } + + return BidderBid.of(bid, bidType, bidResponse.getCur()); + } + + private BidType getBidType(Bid bid, List errors) { + final Integer mType = bid.getMtype(); + if (mType != null) { + return switch (mType) { + case 1 -> BidType.banner; + case 2 -> BidType.video; + default -> { + errors.add(BidderError.badServerResponse("Unsupported media type: %d".formatted(mType))); + yield null; + } + }; + } + + final BidType bidType = Optional.ofNullable(bid.getExt()) + .map(ext -> mapper.mapper().convertValue(ext, EXT_PREBID_TYPE_REFERENCE)) + .map(ExtPrebid::getPrebid) + .map(ExtBidPrebid::getType) + .orElse(null); + + return switch (bidType) { + case banner -> BidType.banner; + case video -> BidType.video; + case null, default -> { + errors.add(BidderError.badServerResponse("Unsupported media type: %s".formatted(bidType))); + yield null; + } + }; + } + +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/synapsehx/ExtImpSynapseHX.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/synapsehx/ExtImpSynapseHX.java new file mode 100644 index 00000000000..83a9d08d9e5 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/synapsehx/ExtImpSynapseHX.java @@ -0,0 +1,11 @@ +package org.prebid.server.proto.openrtb.ext.request.synapsehx; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtImpSynapseHX { + + @JsonProperty("tenantId") + String tenantId; +} diff --git a/src/main/java/org/prebid/server/spring/config/bidder/SynapseHXBidderConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/SynapseHXBidderConfiguration.java new file mode 100644 index 00000000000..ebe1b7f8247 --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/SynapseHXBidderConfiguration.java @@ -0,0 +1,41 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +import org.prebid.server.bidder.synapsehx.SynapseHXBidder; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties; +import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler; +import org.prebid.server.spring.config.bidder.util.UsersyncerCreator; +import org.prebid.server.spring.env.YamlPropertySourceFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import jakarta.validation.constraints.NotBlank; + +@Configuration +@PropertySource(value = "classpath:/bidder-config/synapsehx.yaml", factory = YamlPropertySourceFactory.class) +public class SynapseHXBidderConfiguration { + + private static final String BIDDER_NAME = "synapsehx"; + + @Bean("synapsehxConfigurationProperties") + @ConfigurationProperties("adapters.synapsehx") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps synapsehxBidderDeps(BidderConfigurationProperties synapsehxConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + JacksonMapper mapper) { + + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(synapsehxConfigurationProperties) + .usersyncerCreator(UsersyncerCreator.create(externalUrl)) + .bidderCreator(config -> new SynapseHXBidder(config.getEndpoint(), mapper)) + .assemble(); + } +} diff --git a/src/main/resources/bidder-config/synapsehx.yaml b/src/main/resources/bidder-config/synapsehx.yaml new file mode 100644 index 00000000000..2d13bbfc1ad --- /dev/null +++ b/src/main/resources/bidder-config/synapsehx.yaml @@ -0,0 +1,23 @@ +adapters: + synapsehx: + endpoint: "https://rtb.hx.compasonline.com/pbs" + openrtb-version: 2.6 + meta-info: + maintainer-email: prebid@compas-inc.com + app-media-types: + - banner + - video + site-media-types: + - banner + - video + vendor-id: 0 + usersync: + cookie-family-name: synapsehx + redirect: + url: "https://sync.hx.compasonline.com/pbserver/image?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ccpa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redirect={{redirect_url}}" + uid-macro: "${VISITOR_ID}" + support-cors: false + iframe: + url: "https://sync.hx.compasonline.com/pbserver/iframe?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ccpa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redirect={{redirect_url}}" + uid-macro: "${VISITOR_ID}" + support-cors: false diff --git a/src/main/resources/static/bidder-params/synapsehx.json b/src/main/resources/static/bidder-params/synapsehx.json new file mode 100644 index 00000000000..167a0b8f070 --- /dev/null +++ b/src/main/resources/static/bidder-params/synapsehx.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Synapse HX Adapter Params", + "description": "A schema which validates params accepted by the Synapse HX adapter", + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "description": "Synapse HX tenant identifier", + "minLength": 1 + } + }, + "required": ["tenantId"] +} diff --git a/src/test/java/org/prebid/server/bidder/synapsehx/SynapseHXBidderTest.java b/src/test/java/org/prebid/server/bidder/synapsehx/SynapseHXBidderTest.java new file mode 100644 index 00000000000..115957aa3d9 --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/synapsehx/SynapseHXBidderTest.java @@ -0,0 +1,258 @@ +package org.prebid.server.bidder.synapsehx; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.Audio; +import com.iab.openrtb.request.Banner; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Native; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.vertx.core.MultiMap; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.prebid.server.VertxTest; +import org.prebid.server.bidder.model.BidderBid; +import org.prebid.server.bidder.model.BidderCall; +import org.prebid.server.bidder.model.BidderError; +import org.prebid.server.bidder.model.HttpRequest; +import org.prebid.server.bidder.model.HttpResponse; +import org.prebid.server.bidder.model.Result; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.synapsehx.ExtImpSynapseHX; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.HttpUtil; + +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.function.UnaryOperator.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; + +public class SynapseHXBidderTest extends VertxTest { + + private SynapseHXBidder target; + + @BeforeEach + public void setUp() { + target = new SynapseHXBidder("http://test.endpoint.com", jacksonMapper); + } + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException().isThrownBy( + () -> new SynapseHXBidder("invalid_url", jacksonMapper)); + } + + @Test + public void makeHttpRequestsShouldCorrectlyAddHeaders() { + // given + final BidRequest bidRequest = givenBidRequest(identity()); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getHeaders) + .flatExtracting(MultiMap::entries) + .extracting(Map.Entry::getKey, Map.Entry::getValue) + .containsExactlyInAnyOrder( + tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE), + tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()), + tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.6")); + } + + @Test + public void makeHttpRequestsShouldReturnErrorWhenImpressionContainsInvalidBidderParameters() { + // given + final ObjectNode invalidExt = mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())); + + final BidRequest bidRequest = givenBidRequest(identity(), + impBuilder -> impBuilder.id("Imp01").ext(invalidExt)); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).hasSize(1).contains( + BidderError.badInput("Failed to parse bidder parameters")); + } + + @Test + public void makeHttpRequestsShouldReturnErrorWhenImpressionContainsOnlyAudioAndNative() { + // given + final Audio audio = Audio.builder().mimes(List.of("audio/mp3")).build(); + final Native xNative = Native.builder().build(); + + final BidRequest bidRequest = givenBidRequest(identity(), + impBuilder -> impBuilder.id("Imp01").banner(null).video(null).xNative(xNative).audio(audio)); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).hasSize(1).contains( + BidderError.badInput("imp[Imp01]: Unsupported media type, bidder supports only banner and video")); + } + + @Test + public void makeHttpRequestsShouldRemoveAudioAndNativeFromImpressionIfItContainsSupportedMediaTypes() { + // given + final Audio audio = Audio.builder().mimes(List.of("audio/mp3")).build(); + final Native xNative = Native.builder().build(); + final Banner banner = Banner.builder().build(); + final BidRequest bidRequest = givenBidRequest(identity(), + impBuilder -> impBuilder.id("Imp01").audio(audio).banner(banner).xNative(xNative)); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .allSatisfy(impression -> { + assertThat(impression.getAudio()).isEqualTo(null); + assertThat(impression.getXNative()).isEqualTo(null); + assertThat(impression.getBanner()).isEqualTo(banner); + }); + } + + @Test + public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { + // given + final BidderCall httpCall = givenHttpCall(null, "invalid"); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall(null, mapper.writeValueAsString(null)); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall(null, + mapper.writeValueAsString(BidResponse.builder().build())); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReportErrorAndSkipBidIfCannotParseBidType() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall(null, + givenBidResponse( + givenBid("Imp1", BidType.banner), + givenBidWithMType("Imp2", BidType.banner), + givenBid("Imp3", BidType.video), + givenBidWithMType("Imp4", BidType.video), + givenBid("Imp5", BidType.xNative), + givenBidWithMType("Imp6", BidType.xNative), + givenBid("Imp7", BidType.audio), + givenBidWithMType("Imp8", BidType.audio), + givenBid("Imp9", BidType.banner).toBuilder().ext(null).build())); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).hasSize(5).allSatisfy( + error -> assertThat( + error.getMessage()).startsWith("Unsupported media type")); + assertThat(result.getValue()).hasSize(4).allSatisfy( + bid -> assertThat(bid.getBid().getImpid()).isIn(List.of("Imp1", "Imp2", "Imp3", "Imp4"))); + } + + private static BidRequest givenBidRequest(UnaryOperator impCustomizer) { + return givenBidRequest(identity(), impCustomizer); + } + + private static BidRequest givenBidRequest( + UnaryOperator bidRequestCustomizer, + UnaryOperator impCustomizer) { + + return bidRequestCustomizer.apply(BidRequest.builder() + .imp(singletonList(givenImp(impCustomizer)))) + .build(); + } + + private static Imp givenImp(UnaryOperator impCustomizer) { + return givenImp(impCustomizer, ExtImpSynapseHX.of("tenant-id")); + } + + private static Imp givenImp(UnaryOperator impCustomizer, ExtImpSynapseHX extImp) { + return impCustomizer.apply(Imp.builder() + .id("345") + .banner(Banner.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, extImp)))) + .build(); + } + + private static BidderCall givenHttpCall(BidRequest bidRequest, String body) { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(bidRequest).build(), + HttpResponse.of(200, null, body), + null); + } + + private BidderCall givenHttpCall(BidRequest bidRequest, BidResponse bidResponse) + throws JsonProcessingException { + return givenHttpCall(bidRequest, mapper.writeValueAsString(bidResponse)); + } + + private static BidResponse givenBidResponse(Bid... bids) { + return BidResponse.builder().seatbid(singletonList( + SeatBid.builder().bid(asList(bids)).build())).build(); + } + + private static Bid givenBid(String impid, BidType bidType) { + return Bid.builder() + .impid(impid) + .ext(mapper.createObjectNode() + .set("prebid", mapper.createObjectNode() + .put("type", bidType.getName()))) + .build(); + } + + private static Bid givenBidWithMType(String impid, BidType bidType) { + return Bid.builder().impid(impid).mtype(bidType.ordinal() + 1).build(); + } + +} diff --git a/src/test/java/org/prebid/server/it/SynapseHXTest.java b/src/test/java/org/prebid/server/it/SynapseHXTest.java new file mode 100644 index 00000000000..3dd09c50eef --- /dev/null +++ b/src/test/java/org/prebid/server/it/SynapseHXTest.java @@ -0,0 +1,36 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.prebid.server.model.Endpoint; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class SynapseHXTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromSynapseHX() throws IOException, JSONException { + // given + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/synapsehx-exchange")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/synapsehx/test-synapsehx-bid-request.json"))) + .willReturn(aResponse().withBody(jsonFrom("openrtb2/synapsehx/test-synapsehx-bid-response.json")))); + + // when + final Response response = responseFor("openrtb2/synapsehx/test-auction-synapsehx-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/synapsehx/test-auction-synapsehx-response.json", + response, singletonList("synapsehx")); + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-request.json new file mode 100644 index 00000000000..453cb24289b --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-request.json @@ -0,0 +1,63 @@ +{ + "id": "tid", + "imp": [ + { + "id": "impId001", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "synapsehx": { + "tenantId": "tenant-id-0", + "adUnitId": "ad-unit-id-0" + } + } + } + ], + "device": { + "pxratio": 4.2, + "dnt": 2, + "language": "en", + "ifa": "ifaId" + }, + "site": { + "publisher": { + "id": "publisherId" + } + }, + "at": 1, + "tmax": 5000, + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + } + }, + "auctiontimestamp": 1000 + } + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-response.json b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-response.json new file mode 100644 index 00000000000..759e7c2ac9b --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-auction-synapsehx-response.json @@ -0,0 +1,51 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "id": "bid001", + "impid": "impId001", + "price": 3.33, + "adm": "adm001", + "adid": "adid001", + "cid": "cid001", + "crid": "crid001", + "w": 300, + "h": 250, + "exp" : 300, + "mtype": 1, + "ext": { + "prebid": { + "type": "banner", + "targeting": { + "hb_pb": "3.30", + "hb_size_synapsehx": "300x250", + "hb_bidder_synapsehx": "synapsehx", + "hb_size": "300x250", + "hb_bidder": "synapsehx", + "hb_pb_synapsehx": "3.30" + }, + "meta" : { + "adaptercode" : "synapsehx" + } + }, + "origbidcpm" : 3.33 + } + } + ], + "seat": "synapsehx", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "synapsehx": " synapsehx.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 1000 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-request.json new file mode 100644 index 00000000000..0e74f742127 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-request.json @@ -0,0 +1,84 @@ +{ + "id": "tid", + "imp": [ + { + "id": "impId001", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "secure": 1, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "tenantId": "tenant-id-0", + "adUnitId": "ad-unit-id-0" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "id": "publisherId", + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "dnt": 2, + "ua": "userAgent", + "ip": "193.168.244.1", + "pxratio": 4.2, + "language": "en", + "ifa": "ifaId" + }, + "at": 1, + "tmax": "${json-unit.any-number}", + "cur": [ + "USD" + ], + "source": { + "fd": 1, + "tid": "tid" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true + }, + "auctiontimestamp": 1000, + "channel": { + "name": "web" + }, + "server": { + "externalurl": "http://localhost:8080", + "gvlid": 1, + "datacenter": "local", + "endpoint": "/openrtb2/auction" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-response.json new file mode 100644 index 00000000000..a069a42923f --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/synapsehx/test-synapsehx-bid-response.json @@ -0,0 +1,21 @@ +{ + "id": "tid", + "seatbid": [ + { + "bid": [ + { + "id": "bid001", + "impid": "impId001", + "price": 3.33, + "adid": "adid001", + "crid": "crid001", + "cid": "cid001", + "adm": "adm001", + "h": 250, + "w": 300, + "mtype": 1 + } + ] + } + ] +} 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 1569de649f5..e0c83667f3c 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -571,6 +571,8 @@ adapters.startio.enabled=true adapters.startio.endpoint=http://localhost:8090/startio-exchange adapters.stroeercore..enabled=true adapters.stroeercore.endpoint=http://localhost:8090/stroeercore-exchange +adapters.synapsehx.enabled=true +adapters.synapsehx.endpoint=http://localhost:8090/synapsehx-exchange adapters.suntContent.enabled=true adapters.suntContent.endpoint=http://localhost:8090/suntContent-exchange adapters.taboola.enabled=true