From 80597203e6c37569eb7a6c5fa45193b2cdc07ede Mon Sep 17 00:00:00 2001 From: changjun Date: Thu, 13 Jul 2023 14:29:38 +0900 Subject: [PATCH 1/3] New Adapter: TPMN --- sample/prebid-config.yaml | 2 + .../prebid/server/bidder/tpmn/TpmnBidder.java | 284 +++++++++++ .../openrtb/ext/request/tpmn/ExtImpTpmn.java | 16 + .../config/bidder/TpmnConfiguration.java | 43 ++ src/main/resources/bidder-config/tpmn.yaml | 23 + .../resources/static/bidder-params/tpmn.json | 22 + .../server/bidder/tpmn/TpmnBidderTest.java | 449 ++++++++++++++++++ .../java/org/prebid/server/it/TpmnTest.java | 33 ++ .../tpmn/test-auction-tpmn-request.json | 28 ++ .../test-auction-tpmn-request_origin.json | 27 ++ .../tpmn/test-auction-tpmn-response.json | 38 ++ .../openrtb2/tpmn/test-tpmn-bid-request.json | 59 +++ .../openrtb2/tpmn/test-tpmn-bid-response.json | 20 + .../server/it/test-application.properties | 2 + 14 files changed, 1046 insertions(+) create mode 100644 src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java create mode 100644 src/main/java/org/prebid/server/spring/config/bidder/TpmnConfiguration.java create mode 100644 src/main/resources/bidder-config/tpmn.yaml create mode 100644 src/main/resources/static/bidder-params/tpmn.json create mode 100644 src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java create mode 100644 src/test/java/org/prebid/server/it/TpmnTest.java create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-response.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-response.json diff --git a/sample/prebid-config.yaml b/sample/prebid-config.yaml index 509b0360c39..05ff93c827b 100644 --- a/sample/prebid-config.yaml +++ b/sample/prebid-config.yaml @@ -10,6 +10,8 @@ adapters: enabled: true rubicon: enabled: true + tpmn: + enabled: true metrics: prefix: prebid cache: diff --git a/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java b/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java new file mode 100644 index 00000000000..6d2c66aca44 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java @@ -0,0 +1,284 @@ +package org.prebid.server.bidder.tpmn; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.MissingNode; +import com.iab.openrtb.request.*; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.prebid.server.bidder.Bidder; +import org.prebid.server.bidder.model.*; +import org.prebid.server.currency.CurrencyConversionService; +import org.prebid.server.exception.PreBidException; +import org.prebid.server.json.DecodeException; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.tpmn.ExtImpTpmn; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.BidderUtil; +import org.prebid.server.util.HttpUtil; +import org.prebid.server.util.ObjectUtil; + +import java.math.BigDecimal; +import java.util.*; + +public class TpmnBidder implements Bidder { + + private static final TypeReference> TPMN_EXT_TYPE_REFERENCE = new TypeReference<>() {}; + private static final String BIDDER_CURRENCY = "USD"; + + private final String endpointUrl; + private final JacksonMapper mapper; + private final CurrencyConversionService currencyConversionService; + + public TpmnBidder(String endpointUrl, JacksonMapper mapper, CurrencyConversionService currencyConversionService) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + this.currencyConversionService = Objects.requireNonNull(currencyConversionService); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + final List validImps = new ArrayList<>(); + final List errors = new ArrayList<>(); + for (Imp imp : request.getImp()) { + try { + final ExtImpTpmn extImpTpmn = parseImpExt(imp); + final Imp updatedImp = modifyImp(imp, extImpTpmn, request); + if (updatedImp != null) validImps.add(updatedImp); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + continue; + } + } + + final BidRequest outgoingRequest = request.toBuilder().imp(validImps).build(); + return Result.of(Collections.singletonList(BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper)), errors); + + } + + private ExtImpTpmn parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), TPMN_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException(e.getMessage()); + } + } + + private Imp modifyImp(Imp imp, ExtImpTpmn extImpTpmn, BidRequest request) { + String inventoryId = extImpTpmn.getInventoryId(); + final Imp.ImpBuilder impBuilder = imp.toBuilder().tagid(inventoryId); + final String impId = imp.getId(); + final Price resolvedBidFloor = resolveBidFloor(imp, request); + + if (imp.getBanner() != null) { + impBuilder.id(impId).banner(modifyBanner(imp.getBanner())).video(null).xNative(null); + } else if (imp.getVideo() != null) { + impBuilder.id(impId).xNative(null); + } else if (imp.getXNative() != null) { + impBuilder.id(impId).xNative(modifyNative(imp.getXNative())); + } else { + return null; + } + + return impBuilder + .bidfloor(resolvedBidFloor.getValue()) + .bidfloorcur(resolvedBidFloor.getCurrency()) + .ext(mapper.mapper().valueToTree(extImpTpmn)) + .build(); + } + + + + + private static Banner modifyBanner(Banner banner) { + final Integer w = banner.getW(); + final Integer h = banner.getH(); + final List formats = banner.getFormat(); + + if (w == null || w == 0 || h == null || h == 0) { + if (CollectionUtils.isNotEmpty(formats)) { + final Format firstFormat = formats.get(0); + return banner.toBuilder() + .w(firstFormat.getW()) + .h(firstFormat.getH()) + .build(); + } + + throw new PreBidException("Size information missing for banner"); + } + + return banner; + } + + private Native modifyNative(Native xNative) { + final JsonNode requestNode; + try { + requestNode = mapper.mapper().readTree(xNative.getRequest()); + } catch (JsonProcessingException e) { + throw new PreBidException(e.getMessage()); + } + + final JsonNode nativeNode = requestNode != null + ? requestNode.path("native") + : MissingNode.getInstance(); + + if (nativeNode.isMissingNode()) { + final JsonNode modifiedRequestNode = mapper.mapper().createObjectNode().set("native", requestNode); + return xNative.toBuilder() + .request(mapper.encodeToString(modifiedRequestNode)) + .build(); + } + + return xNative; + } + + + @Override + public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + + final List errors = new ArrayList<>(); + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.withValues(extractBids(bidRequest, bidResponse, errors)); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidRequest bidRequest, + BidResponse bidResponse, + List errors) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(TpmnBidder::isValidBid) + .map(bid -> createBidderBid(bid, bidRequest.getImp(), bidResponse.getCur(), errors)) + .toList(); + } + + private static boolean isValidBid(Bid bid) { + return BidderUtil.isValidPrice(ObjectUtil.getIfNotNull(bid, Bid::getPrice)); + } + + private static BidderBid createBidderBid(Bid bid, List imps, String currency, List errors) { + if (StringUtils.isNotEmpty(currency)) { + currency = BIDDER_CURRENCY; + } + final BidType bidType = getBidType(bid, imps); + if (bidType == null) { + errors.add(BidderError.badServerResponse( + "ignoring bid id=%s, request doesn't contain any valid impression with id=%s" + .formatted(bid.getId(), bid.getImpid()))); + + return null; + } + + return BidderBid.of(bid, bidType, currency); + } + + + private static BidType getBidType(Bid bid, List imps) { + final String impId = bid.getImpid(); + for (Imp imp : imps) { + if (imp.getId().equals(impId)) { + if (imp.getBanner() != null) { + return BidType.banner; + } else if (imp.getVideo() != null) { + return BidType.video; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } + } + } + throw new PreBidException("Failed to find native/banner/video impression " + impId); + } + + + private Price resolveBidFloor(Imp imp, BidRequest bidRequest) { + final Price initialBidFloorPrice = Price.of(imp.getBidfloorcur(), imp.getBidfloor()); + return BidderUtil.shouldConvertBidFloor(initialBidFloorPrice, BIDDER_CURRENCY) + ? convertBidFloor(initialBidFloorPrice, bidRequest) + : initialBidFloorPrice; + } + + private Price convertBidFloor(Price bidFloorPrice, BidRequest bidRequest) { + final BigDecimal convertedPrice = currencyConversionService.convertCurrency( + bidFloorPrice.getValue(), + bidRequest, + bidFloorPrice.getCurrency(), + BIDDER_CURRENCY); + + return Price.of(BIDDER_CURRENCY, convertedPrice); + } + + /** + * 하나의 요청에 여러개의 Imp가 있을 경우 bidRequest는 여러개가 되어 같은 End-Point로 imp개수 만큼 요청을 날린다. + */ + +// private static final String PUBLISHER_ID_MACRO = "{{publisherId}}"; +// private static final String INVENTORY_ID_MACRO = "{{inventoryId}}"; +// @Override +// public Result>> makeHttpRequests(BidRequest request) { +// try { +// validateDevice(request.getDevice()); +// } catch (PreBidException e) { +// return Result.withError(BidderError.badInput(e.getMessage())); +// } +// +// final List> requests = new ArrayList<>(); +// final List errors = new ArrayList<>(); +// +// for (Imp imp : request.getImp()) { +// final ExtImpTpmn extImpTpmn; +// final Imp modifiedImp; +// try { +// extImpTpmn = parseImpExt(imp); +// modifiedImp = modifyImp(imp, extImpTpmn.getInventoryId()); +// } catch (PreBidException e) { +// errors.add(BidderError.badInput(e.getMessage())); +// continue; +// } +// +// if (modifiedImp != null) { +// requests.add(createRequest(request, modifiedImp, extImpTpmn)); +// } +// } +// +// return Result.of(requests, errors); +// } + +// private static void validateDevice(Device device) { +// if (device == null || StringUtils.isEmpty(device.getUa())) { +// throw new PreBidException("Request is missing device UA information"); +// } +// if (device == null || StringUtils.isEmpty(device.getOs())) { +// throw new PreBidException("Request is missing device OS information"); +// } +// } +// +// private HttpRequest createRequest(BidRequest bidRequest, Imp imp, ExtImpTpmn extImpTpmn) { +// final BidRequest outgoingRequest = bidRequest.toBuilder() +// .imp(Collections.singletonList(imp)) +// .build(); +// +// return BidderUtil.defaultRequest(outgoingRequest, resolveUrl(extImpTpmn), mapper); +// } +// +// private String resolveUrl(ExtImpTpmn extImpTpmn) { +// return endpointUrl +// .replace(PUBLISHER_ID_MACRO, HttpUtil.encodeUrl(extImpTpmn.getPublisherId())) +// .replace(INVENTORY_ID_MACRO, HttpUtil.encodeUrl(extImpTpmn.getInventoryId())); +// } +} + diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java new file mode 100644 index 00000000000..eadec312bdd --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java @@ -0,0 +1,16 @@ +package org.prebid.server.proto.openrtb.ext.request.tpmn; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Value; + +@Value +@AllArgsConstructor(staticName = "of") +public class ExtImpTpmn { + + @JsonProperty("inventoryId") + String inventoryId; + + @JsonProperty("publisherId") + String publisherId; +} diff --git a/src/main/java/org/prebid/server/spring/config/bidder/TpmnConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/TpmnConfiguration.java new file mode 100644 index 00000000000..6dd12723f5a --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/TpmnConfiguration.java @@ -0,0 +1,43 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +import org.prebid.server.bidder.tpmn.TpmnBidder; +import org.prebid.server.currency.CurrencyConversionService; +import org.prebid.server.json.JacksonMapper; +import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties; +import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler; +import org.prebid.server.spring.config.bidder.util.UsersyncerCreator; +import org.prebid.server.spring.env.YamlPropertySourceFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import javax.validation.constraints.NotBlank; + +@Configuration +@PropertySource(value = "classpath:/bidder-config/tpmn.yaml", factory = YamlPropertySourceFactory.class) +public class TpmnConfiguration { + + private static final String BIDDER_NAME = "tpmn"; + + @Bean("tpmnConfigurationProperties") + @ConfigurationProperties("adapters.tpmn") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps tpmnBidderDeps(BidderConfigurationProperties tpmnConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + CurrencyConversionService currencyConversionService, + JacksonMapper mapper) { + + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(tpmnConfigurationProperties) + .usersyncerCreator(UsersyncerCreator.create(externalUrl)) + .bidderCreator(config -> new TpmnBidder(config.getEndpoint(), mapper, currencyConversionService)) + .assemble(); + } +} diff --git a/src/main/resources/bidder-config/tpmn.yaml b/src/main/resources/bidder-config/tpmn.yaml new file mode 100644 index 00000000000..5d8380bdfc8 --- /dev/null +++ b/src/main/resources/bidder-config/tpmn.yaml @@ -0,0 +1,23 @@ +adapters: + tpmn: + #endpoint: https://gat.tpmn.io/ortb/pbs_bidder + endpoint: http://localhost:8081/ortb/pbs_bidder + endpoint-compression: gzip + modifyingVastXmlAllowed: true + meta-info: + maintainer-email: prebid@tpmn.io + app-media-types: + - banner + - video + - native + site-media-types: + - banner + - video + - native + supported-vendors: + vendor-id: 0 + usersync: + cookie-family-name: tpmn + redirect: + url: https://gat.tpmn.io/sync/redirect?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redir={{redirect_url}} + support-cors: false diff --git a/src/main/resources/static/bidder-params/tpmn.json b/src/main/resources/static/bidder-params/tpmn.json new file mode 100644 index 00000000000..c8775fbb92f --- /dev/null +++ b/src/main/resources/static/bidder-params/tpmn.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TPMN Adapter Params", + "description": "A schema which validates params accepted by the TPMN adapter", + "type": "object", + "properties": { + "inventoryId": { + "description": "Inventory ID", + "type": "string", + "minLength": 1 + }, + "inventoryId": { + "description": "Publisher ID", + "type": "string", + "minLength": 1 + } + }, + "required": [ + "inventoryId", + "publisherId" + ] +} diff --git a/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java b/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java new file mode 100644 index 00000000000..7d492265700 --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java @@ -0,0 +1,449 @@ +package org.prebid.server.bidder.tpmn; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.iab.openrtb.request.*; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.prebid.server.VertxTest; +import org.prebid.server.bidder.model.*; +import org.prebid.server.currency.CurrencyConversionService; +import org.prebid.server.proto.openrtb.ext.ExtPrebid; +import org.prebid.server.proto.openrtb.ext.request.tpmn.ExtImpTpmn; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +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.BDDAssertions.tuple; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; + +public class TpmnBidderTest extends VertxTest { + + private static final String ENDPOINT_URL = "https://randomurl.com/"; + + private TpmnBidder tpmnBidder; + + @Rule + public final MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock + private CurrencyConversionService currencyConversionService; + + @Before + public void setUp() { + tpmnBidder = new TpmnBidder(ENDPOINT_URL, jacksonMapper, currencyConversionService); + } + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException().isThrownBy(() -> new TpmnBidder("invalid_url", jacksonMapper, currencyConversionService)); + } + + @Test + public void makeHttpRequestsShouldConvertCurrencyIfRequestCurrencyDoesNotMatchBidderCurrency() { + // given + given(currencyConversionService.convertCurrency(any(), any(), anyString(), anyString())) + .willReturn(BigDecimal.TEN); + + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder + .banner(Banner.builder().w(5).h(5).build()) + .bidfloor(BigDecimal.ONE).bidfloorcur("USD")); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getBidfloor, Imp::getBidfloorcur) + .containsExactly(tuple(BigDecimal.ONE, "USD")); + + } + +// @Test +// public void makeHttpRequestsShouldReturnErrorIfDeviceIsAbsent() { +// // given +// final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> bidRequestBuilder.device(null), identity()); +// +// // when +// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); +// +// // then +// assertThat(result.getValue()).isEmpty(); +// assertThat(result.getErrors()) +// .containsAnyOf(BidderError.badInput("Request is missing device UA information"), +// BidderError.badInput("Request is missing device OS information")); +// +// +// } + +// @Test +// public void makeHttpRequestsShouldReturnErrorIfDeviceOsIsAbsent() { +// // given +// final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> +// bidRequestBuilder.device(Device.builder().build()), identity()); +// +// // when +// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); +// +// // then +// assertThat(result.getValue()).isEmpty(); +// assertThat(result.getErrors()) +// .containsAnyOf(BidderError.badInput("Request is missing device UA information"), +// BidderError.badInput("Request is missing device OS information")); +// } + + @Test + public void makeHttpRequestsShouldTakeSizesFromFormatIfBannerSizesNotExists() { + // given + final Banner banner = Banner.builder().format(singletonList(Format.builder().h(1).w(1).build())).build(); + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.banner(banner)); + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getBanner) + .containsExactly(banner.toBuilder().w(1).h(1).build()); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfBannerHasNoSizeParametersAndFormatIsEmpty() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.banner(Banner.builder().build())); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + //assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).containsExactly(BidderError.badInput("Size information missing for banner")); + } + + @Test + public void makeHttpRequestsShouldNotModifyNativeIfNativeIsPresentInNativeRequest() throws JsonProcessingException { + // given + final ObjectNode nativeRequestNode = mapper.createObjectNode().set("native", TextNode.valueOf("test")); + final String nativeRequest = mapper.writeValueAsString(nativeRequestNode); + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder + .xNative(Native.builder().request(nativeRequest).build())); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getXNative) + .containsExactly(Native.builder().request(nativeRequest).build()); + } + + @Test + public void makeHttpRequestsShouldCorrectlyResolveNative() throws JsonProcessingException { + // given + final ObjectNode nativeRequestNode = mapper.createObjectNode() + .set("test", TextNode.valueOf("test")); + final String nativeRequest = mapper.writeValueAsString(nativeRequestNode); + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder + .xNative(Native.builder().request(nativeRequest).build())); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getXNative) + .containsExactly(Native.builder() + .request(mapper.writeValueAsString(mapper.createObjectNode().set("native", + mapper.createObjectNode().set("test", TextNode.valueOf("test"))))) + .build()); + } + + @Test + public void makeHttpRequestsReturnErrorIfNativeCouldNotBeParsed() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder + .xNative(Native.builder().request("invalid_native").build())); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(bidderError -> { + assertThat(bidderError.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(bidderError.getMessage()).startsWith("Unrecognized token"); + }); + } + + @Test + public void makeHttpRequestsShouldReturnErrorsOnNotValidImps() { + // given + final BidRequest bidRequest = givenBidRequest(identity(), + impBuilder -> impBuilder + .id("234") + .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))), + impBuilder -> impBuilder + .id("123") + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId", "publisherId"))))); + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getTagid) + .containsExactly("inventoryId"); + assertThat(result.getErrors()).allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()).startsWith("Cannot deserialize value of type"); + }); + } + +// @Test +// public void makeHttpRequestsShouldCreateCorrectURL() { +// // given +// final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder +// .id("123") +// .video(Video.builder().build()) +// .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of("inventoryId","publisherId"))))); +// +// // when +// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); +// +// // then +// assertThat(result.getErrors()).isEmpty(); +// assertThat(result.getValue()).hasSize(1) +// .extracting(HttpRequest::getUri) +// .containsExactly("https://randomurl.com/publisherId/inventoryId"); +// } + + @Test + public void makeHttpRequestsShouldCreateRequestPerImp() { + // given + final BidRequest bidRequest = givenBidRequest( + identity(), + impBuilder -> impBuilder + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId")))), + impBuilder -> impBuilder + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId"))))); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getImp) + .extracting(List::size) + .containsOnly(2); + } + + @Test + public void makeHttpRequestsShouldSkipImpWithoutBannerOrVideoOrNative() { + // given + final BidRequest bidRequest = givenBidRequest( + identity(), + impBuilder -> impBuilder + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId")))), + impBuilder -> impBuilder + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId"))))); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .hasSize(1); + } + + @Test + public void makeHttpRequestsShouldCorrectlyModifyImpId() { + // given + final BidRequest bidRequest = givenBidRequest(identity(), + impBuilder -> impBuilder + .id("id1") + .banner(Banner.builder().w(5).h(5).build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId")))), + impBuilder -> impBuilder + .id("id2") + .video(Video.builder().build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId")))), + impBuilder -> impBuilder + .id("id3") + .xNative(Native.builder().request("{}").build()) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( + "inventoryId","publisherId"))))); + + // when + final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getId) + .containsExactly("id1", "id2", "id3"); + } + + @Test + public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { + // given + final BidderCall httpCall = givenHttpCall(null, "invalid"); + + // when + final Result> result = tpmnBidder.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 = tpmnBidder.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 = tpmnBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + + @Test + public void makeBidsShouldOmitBidsWithNullPrice() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(identity()); + final BidderCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString( + givenBidResponse(bidBuilder -> bidBuilder.impid("123").price(null)))); + + // when + final Result> result = tpmnBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldOmitBidsWithPriceLessOrEqualToZero() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(identity()); + final BidderCall httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString( + givenBidResponse(bidBuilder -> bidBuilder.impid("123").price(BigDecimal.valueOf(-1)), + bidBuilder -> bidBuilder.impid("123").price(BigDecimal.ZERO)))); + + // when + final Result> result = tpmnBidder.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + private static BidRequest givenBidRequest( + Function bidRequestCustomizer, + Function... impCustomizers) { + + return bidRequestCustomizer.apply(BidRequest.builder() + .imp(Arrays.stream(impCustomizers) + .map(TpmnBidderTest::givenImp) + .toList()) + .device(Device.builder().os("deviceOs").ua("some-ua").build())) + .build(); + } + + private static BidRequest givenBidRequest(Function impCustomizer) { + return givenBidRequest(identity(), impCustomizer); + } + + private static Imp givenImp(Function impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("123") + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( "inventoryId","publisherId"))))).build(); + } + + private static BidResponse givenBidResponse(Function... bidCustomizers) { + return BidResponse.builder() + .seatbid(singletonList(SeatBid.builder() + .bid(Arrays.stream(bidCustomizers) + .map(customizer -> customizer.apply(Bid.builder()).build()) + .toList()) + .build())) + .build(); + } + + private static BidderCall givenHttpCall(BidRequest bidRequest, String body) { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(bidRequest).build(), + HttpResponse.of(200, null, body), + null); + } +} + diff --git a/src/test/java/org/prebid/server/it/TpmnTest.java b/src/test/java/org/prebid/server/it/TpmnTest.java new file mode 100644 index 00000000000..9621a2b3844 --- /dev/null +++ b/src/test/java/org/prebid/server/it/TpmnTest.java @@ -0,0 +1,33 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.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.*; +import static java.util.Collections.singletonList; + +@RunWith(SpringRunner.class) +public class TpmnTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromTpmn() throws IOException, JSONException { + // given + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/tpmn-exchange")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/tpmn/test-tpmn-bid-request.json"))) + .willReturn(aResponse().withBody(jsonFrom("openrtb2/tpmn/test-tpmn-bid-response.json")))); + + // when + final Response response = responseFor("openrtb2/tpmn/test-auction-tpmn-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/tpmn/test-auction-tpmn-response.json", response, + singletonList("tpmn")); + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json new file mode 100644 index 00000000000..20dfd7a22d3 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json @@ -0,0 +1,28 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "tpmn": { + "inventoryId": "some-inventoryId", + "publisherId": "some-publisherId" + } + } + } + ], + "device": { + "os": "some-Os", + "ua": "some-agent" + }, + "tmax": 5000, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json new file mode 100644 index 00000000000..51159235602 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json @@ -0,0 +1,27 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "tpmn": { + "publisherId": "publisherId", + "inventoryId": "inventoryId" + } + } + } + ], + "device": { + "os": "deviceOs" + }, + "tmax": 5000, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-response.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-response.json new file mode 100644 index 00000000000..872c0500b42 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-response.json @@ -0,0 +1,38 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 3.33, + "adm": "adm001", + "adid": "adid001", + "cid": "cid001", + "crid": "crid001", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + }, + "origbidcpm": 3.33 + } + } + ], + "seat": "tpmn", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "tpmn": "{{ tpmn.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json new file mode 100644 index 00000000000..cbdb51ebf6a --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json @@ -0,0 +1,59 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "tagid": "some-inventoryId", + "secure": 1, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "inventoryId": "some-inventoryId", + "publisherId": "some-publisherId" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "some-agent", + "ip": "193.168.244.1", + "os": "some-Os" + }, + "at": 1, + "tmax": "${json-unit.any-number}", + "cur": [ + "USD" + ], + "source": { + "tid": "${json-unit.any-string}" + }, + "regs": { + "ext": { + "gdpr": 0 + } + }, + "ext": { + "prebid": { + "server": { + "externalurl": "http://localhost:8080", + "gvlid": 1, + "datacenter": "local", + "endpoint": "/openrtb2/auction" + } + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-response.json new file mode 100644 index 00000000000..a4c0edc3e09 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-response.json @@ -0,0 +1,20 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 3.33, + "adid": "adid001", + "crid": "crid001", + "cid": "cid001", + "adm": "adm001", + "h": 250, + "w": 300 + } + ] + } + ] +} 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 65dc53cdde4..944c31b994f 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -333,6 +333,8 @@ adapters.thirtythreeacross.endpoint=http://localhost:8090/thirtythreeacross-exch adapters.thirtythreeacross.partner-id=partner adapters.thirtythreeacross.aliases.33across.enabled=true adapters.thirtythreeacross.aliases.ttx.enabled=true +adapters.tpmn.enabled=true +adapters.tpmn.endpoint=http://localhost:8090/tpmn-exchange adapters.trafficgate.enabled=true adapters.trafficgate.endpoint=http://localhost:8090/trafficgate-exchange adapters.ucfunnel.enabled=true From 5bbc25c25ba54bdc9d4798723295cb7f695ccf80 Mon Sep 17 00:00:00 2001 From: changjun Date: Tue, 18 Jul 2023 10:29:59 +0900 Subject: [PATCH 2/3] New Adapter: TPMN - remove publisherId request param --- .../prebid/server/bidder/tpmn/TpmnBidder.java | 62 +-------------- .../openrtb/ext/request/tpmn/ExtImpTpmn.java | 4 +- .../resources/static/bidder-params/tpmn.json | 10 +-- .../server/bidder/tpmn/TpmnBidderTest.java | 78 +++---------------- .../tpmn/test-auction-tpmn-request.json | 3 +- .../test-auction-tpmn-request_origin.json | 27 ------- .../openrtb2/tpmn/test-tpmn-bid-request.json | 3 +- 7 files changed, 17 insertions(+), 170 deletions(-) delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json diff --git a/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java b/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java index 6d2c66aca44..6932ff3e13b 100644 --- a/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java +++ b/src/main/java/org/prebid/server/bidder/tpmn/TpmnBidder.java @@ -70,8 +70,8 @@ private ExtImpTpmn parseImpExt(Imp imp) { } private Imp modifyImp(Imp imp, ExtImpTpmn extImpTpmn, BidRequest request) { - String inventoryId = extImpTpmn.getInventoryId(); - final Imp.ImpBuilder impBuilder = imp.toBuilder().tagid(inventoryId); + Integer inventoryId = extImpTpmn.getInventoryId(); + final Imp.ImpBuilder impBuilder = imp.toBuilder().tagid(String.valueOf(inventoryId)); final String impId = imp.getId(); final Price resolvedBidFloor = resolveBidFloor(imp, request); @@ -222,63 +222,5 @@ private Price convertBidFloor(Price bidFloorPrice, BidRequest bidRequest) { return Price.of(BIDDER_CURRENCY, convertedPrice); } - /** - * 하나의 요청에 여러개의 Imp가 있을 경우 bidRequest는 여러개가 되어 같은 End-Point로 imp개수 만큼 요청을 날린다. - */ - -// private static final String PUBLISHER_ID_MACRO = "{{publisherId}}"; -// private static final String INVENTORY_ID_MACRO = "{{inventoryId}}"; -// @Override -// public Result>> makeHttpRequests(BidRequest request) { -// try { -// validateDevice(request.getDevice()); -// } catch (PreBidException e) { -// return Result.withError(BidderError.badInput(e.getMessage())); -// } -// -// final List> requests = new ArrayList<>(); -// final List errors = new ArrayList<>(); -// -// for (Imp imp : request.getImp()) { -// final ExtImpTpmn extImpTpmn; -// final Imp modifiedImp; -// try { -// extImpTpmn = parseImpExt(imp); -// modifiedImp = modifyImp(imp, extImpTpmn.getInventoryId()); -// } catch (PreBidException e) { -// errors.add(BidderError.badInput(e.getMessage())); -// continue; -// } -// -// if (modifiedImp != null) { -// requests.add(createRequest(request, modifiedImp, extImpTpmn)); -// } -// } -// -// return Result.of(requests, errors); -// } - -// private static void validateDevice(Device device) { -// if (device == null || StringUtils.isEmpty(device.getUa())) { -// throw new PreBidException("Request is missing device UA information"); -// } -// if (device == null || StringUtils.isEmpty(device.getOs())) { -// throw new PreBidException("Request is missing device OS information"); -// } -// } -// -// private HttpRequest createRequest(BidRequest bidRequest, Imp imp, ExtImpTpmn extImpTpmn) { -// final BidRequest outgoingRequest = bidRequest.toBuilder() -// .imp(Collections.singletonList(imp)) -// .build(); -// -// return BidderUtil.defaultRequest(outgoingRequest, resolveUrl(extImpTpmn), mapper); -// } -// -// private String resolveUrl(ExtImpTpmn extImpTpmn) { -// return endpointUrl -// .replace(PUBLISHER_ID_MACRO, HttpUtil.encodeUrl(extImpTpmn.getPublisherId())) -// .replace(INVENTORY_ID_MACRO, HttpUtil.encodeUrl(extImpTpmn.getInventoryId())); -// } } diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java index eadec312bdd..dfc370263d2 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/tpmn/ExtImpTpmn.java @@ -9,8 +9,6 @@ public class ExtImpTpmn { @JsonProperty("inventoryId") - String inventoryId; + Integer inventoryId; - @JsonProperty("publisherId") - String publisherId; } diff --git a/src/main/resources/static/bidder-params/tpmn.json b/src/main/resources/static/bidder-params/tpmn.json index c8775fbb92f..99f42ad660a 100644 --- a/src/main/resources/static/bidder-params/tpmn.json +++ b/src/main/resources/static/bidder-params/tpmn.json @@ -6,17 +6,11 @@ "properties": { "inventoryId": { "description": "Inventory ID", - "type": "string", - "minLength": 1 - }, - "inventoryId": { - "description": "Publisher ID", - "type": "string", + "type": "integer", "minLength": 1 } }, "required": [ - "inventoryId", - "publisherId" + "inventoryId" ] } diff --git a/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java b/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java index 7d492265700..53046f3fecf 100644 --- a/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/tpmn/TpmnBidderTest.java @@ -7,7 +7,6 @@ import com.iab.openrtb.response.Bid; import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; -import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -80,38 +79,6 @@ public void makeHttpRequestsShouldConvertCurrencyIfRequestCurrencyDoesNotMatchBi } -// @Test -// public void makeHttpRequestsShouldReturnErrorIfDeviceIsAbsent() { -// // given -// final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> bidRequestBuilder.device(null), identity()); -// -// // when -// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); -// -// // then -// assertThat(result.getValue()).isEmpty(); -// assertThat(result.getErrors()) -// .containsAnyOf(BidderError.badInput("Request is missing device UA information"), -// BidderError.badInput("Request is missing device OS information")); -// -// -// } - -// @Test -// public void makeHttpRequestsShouldReturnErrorIfDeviceOsIsAbsent() { -// // given -// final BidRequest bidRequest = givenBidRequest(bidRequestBuilder -> -// bidRequestBuilder.device(Device.builder().build()), identity()); -// -// // when -// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); -// -// // then -// assertThat(result.getValue()).isEmpty(); -// assertThat(result.getErrors()) -// .containsAnyOf(BidderError.badInput("Request is missing device UA information"), -// BidderError.badInput("Request is missing device OS information")); -// } @Test public void makeHttpRequestsShouldTakeSizesFromFormatIfBannerSizesNotExists() { @@ -214,8 +181,7 @@ public void makeHttpRequestsShouldReturnErrorsOnNotValidImps() { impBuilder -> impBuilder .id("123") .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId", "publisherId"))))); + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001))))); // when final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); @@ -224,30 +190,13 @@ public void makeHttpRequestsShouldReturnErrorsOnNotValidImps() { .extracting(HttpRequest::getPayload) .flatExtracting(BidRequest::getImp) .extracting(Imp::getTagid) - .containsExactly("inventoryId"); + .containsExactly(String.valueOf(10000001)); assertThat(result.getErrors()).allSatisfy(error -> { assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); assertThat(error.getMessage()).startsWith("Cannot deserialize value of type"); }); } -// @Test -// public void makeHttpRequestsShouldCreateCorrectURL() { -// // given -// final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder -// .id("123") -// .video(Video.builder().build()) -// .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of("inventoryId","publisherId"))))); -// -// // when -// final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); -// -// // then -// assertThat(result.getErrors()).isEmpty(); -// assertThat(result.getValue()).hasSize(1) -// .extracting(HttpRequest::getUri) -// .containsExactly("https://randomurl.com/publisherId/inventoryId"); -// } @Test public void makeHttpRequestsShouldCreateRequestPerImp() { @@ -256,12 +205,10 @@ public void makeHttpRequestsShouldCreateRequestPerImp() { identity(), impBuilder -> impBuilder .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId")))), + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001)))), impBuilder -> impBuilder .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId"))))); + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001))))); // when final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); @@ -281,12 +228,10 @@ public void makeHttpRequestsShouldSkipImpWithoutBannerOrVideoOrNative() { final BidRequest bidRequest = givenBidRequest( identity(), impBuilder -> impBuilder - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId")))), + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001)))), impBuilder -> impBuilder .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId"))))); + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001))))); // when final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); @@ -306,18 +251,15 @@ public void makeHttpRequestsShouldCorrectlyModifyImpId() { impBuilder -> impBuilder .id("id1") .banner(Banner.builder().w(5).h(5).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId")))), + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001)))), impBuilder -> impBuilder .id("id2") .video(Video.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId")))), + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001)))), impBuilder -> impBuilder .id("id3") .xNative(Native.builder().request("{}").build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( - "inventoryId","publisherId"))))); + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001))))); // when final Result>> result = tpmnBidder.makeHttpRequests(bidRequest); @@ -426,7 +368,7 @@ private static BidRequest givenBidRequest(Function impCustomizer) { return impCustomizer.apply(Imp.builder() .id("123") - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of( "inventoryId","publisherId"))))).build(); + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpTpmn.of(10000001))))).build(); } private static BidResponse givenBidResponse(Function... bidCustomizers) { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json index 20dfd7a22d3..bedd11e7568 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request.json @@ -9,8 +9,7 @@ }, "ext": { "tpmn": { - "inventoryId": "some-inventoryId", - "publisherId": "some-publisherId" + "inventoryId": 10000001 } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json deleted file mode 100644 index 51159235602..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-auction-tpmn-request_origin.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "id": "request_id", - "imp": [ - { - "id": "imp_id", - "banner": { - "w": 300, - "h": 250 - }, - "ext": { - "tpmn": { - "publisherId": "publisherId", - "inventoryId": "inventoryId" - } - } - } - ], - "device": { - "os": "deviceOs" - }, - "tmax": 5000, - "regs": { - "ext": { - "gdpr": 0 - } - } -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json index cbdb51ebf6a..7953b3dd3d1 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/tpmn/test-tpmn-bid-request.json @@ -12,8 +12,7 @@ "ext": { "tid": "${json-unit.any-string}", "bidder": { - "inventoryId": "some-inventoryId", - "publisherId": "some-publisherId" + "inventoryId": 10000001 } } } From daa10833ca42e96e9b9bd190f77ff54a2ba76aba Mon Sep 17 00:00:00 2001 From: changjun Date: Tue, 18 Jul 2023 11:53:32 +0900 Subject: [PATCH 3/3] New Adapter: TPMN - remove publisherId request param --- src/main/resources/bidder-config/tpmn.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/bidder-config/tpmn.yaml b/src/main/resources/bidder-config/tpmn.yaml index 5d8380bdfc8..5de3f9ec0a1 100644 --- a/src/main/resources/bidder-config/tpmn.yaml +++ b/src/main/resources/bidder-config/tpmn.yaml @@ -1,7 +1,6 @@ adapters: tpmn: - #endpoint: https://gat.tpmn.io/ortb/pbs_bidder - endpoint: http://localhost:8081/ortb/pbs_bidder + endpoint: https://gat.tpmn.io/ortb/pbs_bidder endpoint-compression: gzip modifyingVastXmlAllowed: true meta-info: