diff --git a/extra/bundle/pom.xml b/extra/bundle/pom.xml index 5d44255ca2d..a210a0919ee 100644 --- a/extra/bundle/pom.xml +++ b/extra/bundle/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT ../../extra/pom.xml diff --git a/extra/modules/confiant-ad-quality/pom.xml b/extra/modules/confiant-ad-quality/pom.xml index 1d86482129b..e2f82de7886 100644 --- a/extra/modules/confiant-ad-quality/pom.xml +++ b/extra/modules/confiant-ad-quality/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT confiant-ad-quality diff --git a/extra/modules/fiftyone-devicedetection/pom.xml b/extra/modules/fiftyone-devicedetection/pom.xml index aafbfa859ac..5c1dbb9ccc0 100644 --- a/extra/modules/fiftyone-devicedetection/pom.xml +++ b/extra/modules/fiftyone-devicedetection/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT fiftyone-devicedetection diff --git a/extra/modules/greenbids-real-time-data/pom.xml b/extra/modules/greenbids-real-time-data/pom.xml index 141e4cf087e..f5ab8eb4b54 100644 --- a/extra/modules/greenbids-real-time-data/pom.xml +++ b/extra/modules/greenbids-real-time-data/pom.xml @@ -4,7 +4,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT greenbids-real-time-data diff --git a/extra/modules/live-intent-omni-channel-identity/pom.xml b/extra/modules/live-intent-omni-channel-identity/pom.xml index c0c6c463354..e846ccbdad6 100644 --- a/extra/modules/live-intent-omni-channel-identity/pom.xml +++ b/extra/modules/live-intent-omni-channel-identity/pom.xml @@ -4,7 +4,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT live-intent-omni-channel-identity diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java index 01d37eabda0..383aacea3a9 100644 --- a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java +++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/model/config/LiveIntentOmniChannelProperties.java @@ -2,7 +2,7 @@ import lombok.Data; -import java.util.List; +import java.util.Set; @Data public final class LiveIntentOmniChannelProperties { @@ -15,5 +15,5 @@ public final class LiveIntentOmniChannelProperties { float treatmentRate; - List targetBidders; + Set targetBidders; } diff --git a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java index 32b608ce21c..3afdb146018 100644 --- a/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java +++ b/extra/modules/live-intent-omni-channel-identity/src/main/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/hooks/LiveIntentOmniChannelIdentityProcessedAuctionRequestHook.java @@ -9,6 +9,7 @@ import io.vertx.core.MultiMap; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; +import org.apache.commons.collections4.SetUtils; import org.prebid.server.activity.Activity; import org.prebid.server.activity.ComponentType; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; @@ -40,18 +41,15 @@ import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidDataEidPermissions; import org.prebid.server.util.HttpUtil; import org.prebid.server.util.ListUtil; -import org.prebid.server.util.StreamUtil; import org.prebid.server.vertx.httpclient.HttpClient; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; -import java.util.stream.Stream; public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements ProcessedAuctionRequestHook { @@ -65,7 +63,7 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHook implements private final HttpClient httpClient; private final UserFpdActivityMask userFpdActivityMask; private final double logSamplingRate; - private final List targetBidders; + private final Set targetBidders; public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniChannelProperties config, UserFpdActivityMask userFpdActivityMask, @@ -79,7 +77,7 @@ public LiveIntentOmniChannelIdentityProcessedAuctionRequestHook(LiveIntentOmniCh this.httpClient = Objects.requireNonNull(httpClient); this.logSamplingRate = logSamplingRate; this.userFpdActivityMask = Objects.requireNonNull(userFpdActivityMask); - this.targetBidders = ListUtils.emptyIfNull(config.getTargetBidders()); + this.targetBidders = SetUtils.emptyIfNull(config.getTargetBidders()); } @Override @@ -202,7 +200,7 @@ private AuctionRequestPayload updatedPayload(AuctionRequestPayload requestPayloa } private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolvedEids) { - if (targetBidders.isEmpty()) { + if (CollectionUtils.isEmpty(targetBidders) || CollectionUtils.isEmpty(resolvedEids)) { return bidRequest; } @@ -210,6 +208,14 @@ private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolve final ExtRequestPrebid extPrebid = ext != null ? ext.getPrebid() : null; final ExtRequestPrebidData extPrebidData = extPrebid != null ? extPrebid.getData() : null; + final List existingPerms = extPrebidData != null + ? extPrebidData.getEidPermissions() + : null; + + if (CollectionUtils.isEmpty(existingPerms)) { + return bidRequest; + } + final ExtRequestPrebid updatedExtPrebid = Optional.ofNullable(extPrebid) .map(ExtRequestPrebid::toBuilder) .orElseGet(ExtRequestPrebid::builder) @@ -225,35 +231,38 @@ private BidRequest updateAllowedBidders(BidRequest bidRequest, List resolve } private ExtRequestPrebidData updatePrebidData(ExtRequestPrebidData extPrebidData, List resolvedEids) { - final List prebidDataBidders = extPrebidData != null ? extPrebidData.getBidders() : null; - final List updatedPrebidDataBidders = prebidDataBidders != null - ? (List) CollectionUtils.union(targetBidders, prebidDataBidders) - : targetBidders; - - final Set resolvedSources = resolvedEids.stream().map(Eid::getSource).collect(Collectors.toSet()); - - final List initialPermissions = Optional.ofNullable(extPrebidData) - .map(ExtRequestPrebidData::getEidPermissions) - .orElse(Collections.emptyList()); - final List updatedPermissions = Stream.concat( - initialPermissions.stream() - .map(permission -> updateEidPermission(permission, resolvedSources)), - resolvedSources.stream() - .map(source -> ExtRequestPrebidDataEidPermissions.of(source, targetBidders))) - .filter(StreamUtil.distinctBy(ExtRequestPrebidDataEidPermissions::getSource)) + final List originalBidders = extPrebidData != null ? extPrebidData.getBidders() : null; + + final Set resolvedSources = resolvedEids.stream() + .map(Eid::getSource) + .collect(Collectors.toSet()); + + final List updatedPermissions = extPrebidData.getEidPermissions().stream() + .map(permission -> restrictEidPermission(permission, resolvedSources)) + .filter(Objects::nonNull) .toList(); - return ExtRequestPrebidData.of(updatedPrebidDataBidders, updatedPermissions); + return ExtRequestPrebidData.of(originalBidders, updatedPermissions); } - private ExtRequestPrebidDataEidPermissions updateEidPermission(ExtRequestPrebidDataEidPermissions permission, - Set resolvedSources) { + private ExtRequestPrebidDataEidPermissions restrictEidPermission(ExtRequestPrebidDataEidPermissions permission, + Set resolvedSources) { - return resolvedSources.contains(permission.getSource()) - ? ExtRequestPrebidDataEidPermissions.of( - permission.getSource(), - (List) CollectionUtils.union(permission.getBidders(), targetBidders)) - : permission; + if (!resolvedSources.contains(permission.getSource())) { + return permission; + } + + final List finalBidders = ListUtils.emptyIfNull(permission.getBidders()).stream() + .filter(targetBidders::contains) + .toList(); + + return CollectionUtils.isEmpty(finalBidders) + ? null + : ExtRequestPrebidDataEidPermissions + .builder() + .bidders(finalBidders) + .source(permission.getSource()) + .build(); } @Override diff --git a/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java index 7b05ec6072d..4dfaa8c8df4 100644 --- a/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java +++ b/extra/modules/live-intent-omni-channel-identity/src/test/java/org/prebid/server/hooks/modules/liveintent/omni/channel/identity/v1/LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest.java @@ -36,6 +36,7 @@ import org.prebid.server.vertx.httpclient.model.HttpClientResponse; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeoutException; import static java.util.Collections.singletonList; @@ -74,11 +75,11 @@ public class LiveIntentOmniChannelIdentityProcessedAuctionRequestHookTest { private LiveIntentOmniChannelIdentityProcessedAuctionRequestHook target; - private List configuredBidders; + private Set configuredBidders; @BeforeEach public void setUp() { - configuredBidders = List.of("bidder1", "bidder2"); + configuredBidders = Set.of("bidder1", "bidder2"); given(properties.getRequestTimeoutMs()).willReturn(5L); given(properties.getIdentityResolutionEndpoint()).willReturn("https://test.com/idres"); given(properties.getAuthToken()).willReturn("auth_token"); @@ -375,14 +376,38 @@ public void callShouldReturnFailureWhenRequestingEidsIsFailed() { } @Test - public void biddersConfiguredRestrictionShouldBeRespected() { + public void shouldRestrictExistingEidPermissionsByIntersectionAndKeepGlobalBiddersUnchanged() { + // given final Uid givenUid = Uid.builder().id("id1").atype(2).build(); final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); final User givenUser = User.builder().eids(singletonList(givenEid)).build(); - final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).build(); - final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(configuredBidders, List.of( - ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders))); + final ExtRequestPrebidDataEidPermissions otherBidder = ExtRequestPrebidDataEidPermissions.builder() + .source("some.other-source.com") + .bidders(singletonList("bidderY")) + .build(); + + final ExtRequestPrebidDataEidPermissions liBidder2 = ExtRequestPrebidDataEidPermissions.builder() + .source("liveintent.com") + .bidders(singletonList("bidder2")) + .build(); + final ExtRequestPrebidDataEidPermissions liBidder23 = ExtRequestPrebidDataEidPermissions.builder() + .source("liveintent.com") + .bidders(List.of("bidder2", "bidder3")) + .build(); + + final ExtRequestPrebidData givenData = ExtRequestPrebidData.of(singletonList("bidderX"), + List.of(otherBidder, liBidder23)); + + final BidRequest givenBidRequest = BidRequest.builder() + .id("request") + .user(givenUser) + .ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build())) + .build(); + + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of( + List.of("bidderX"), + List.of(otherBidder, liBidder2)); final Eid expectedEid = Eid.builder().source("liveintent.com").build(); @@ -418,23 +443,31 @@ public void biddersConfiguredRestrictionShouldBeRespected() { } @Test - public void biddersConfiguredRestrictionShouldBeMergedWithProvided() { + public void shouldNotAddNewEidPermissionsOrModifyGlobalBiddersWhenSourceNotPresent() { // given final Uid givenUid = Uid.builder().id("id1").atype(2).build(); final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); final User givenUser = User.builder().eids(singletonList(givenEid)).build(); - final BidRequest givenBidRequest = BidRequest.builder().id("request").user(givenUser).ext(ExtRequest.of( - ExtRequestPrebid.builder().data(ExtRequestPrebidData.of(List.of("bidder3"), List.of( - ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")), - ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")))) - ).build())).build(); + final ExtRequestPrebidDataEidPermissions bidder1 = ExtRequestPrebidDataEidPermissions.builder() + .source("some.other-source.com") + .bidders(singletonList("bidder3")) + .build(); + final ExtRequestPrebidDataEidPermissions bidder2 = ExtRequestPrebidDataEidPermissions.builder() + .source("some.source.com") + .bidders(singletonList("bidder3")) + .build(); - final List expectedBidders = List.of("bidder3", "bidder2", "bidder1"); + final List bidders = List.of(bidder1, bidder2); - final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(expectedBidders, List.of( - ExtRequestPrebidDataEidPermissions.of("some.other-source.com", List.of("bidder3")), - ExtRequestPrebidDataEidPermissions.of("some.source.com", List.of("bidder3")), - ExtRequestPrebidDataEidPermissions.of("liveintent.com", configuredBidders))); + final BidRequest givenBidRequest = BidRequest.builder() + .id("request") + .user(givenUser) + .ext(ExtRequest.of(ExtRequestPrebid.builder() + .data(ExtRequestPrebidData.of(singletonList("bidder3"), bidders)) + .build())) + .build(); + + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of(List.of("bidder3"), bidders); final Eid expectedEid = Eid.builder().source("liveintent.com").build(); @@ -468,4 +501,69 @@ public void biddersConfiguredRestrictionShouldBeMergedWithProvided() { eq(MAPPER.encodeToString(givenBidRequest)), eq(5L)); } + + @Test + public void shouldRemovePermissionWhenIntersectionIsEmpty() { + // given + final Uid givenUid = Uid.builder().id("id1").atype(2).build(); + final Eid givenEid = Eid.builder().source("some.source.com").uids(singletonList(givenUid)).build(); + final User givenUser = User.builder().eids(singletonList(givenEid)).build(); + + final ExtRequestPrebidData givenData = ExtRequestPrebidData.of( + List.of("bidderGlobal"), + List.of( + ExtRequestPrebidDataEidPermissions.builder() + .source("liveintent.com") + .bidders(singletonList("not-allowed")) + .build(), + ExtRequestPrebidDataEidPermissions.builder() + .source("keep.com") + .bidders(singletonList("bidderGlobal")) + .build())); + + final BidRequest givenBidRequest = BidRequest.builder() + .id("request") + .user(givenUser) + .ext(ExtRequest.of(ExtRequestPrebid.builder().data(givenData).build())) + .build(); + + final Eid expectedEid = Eid.builder().source("liveintent.com").build(); + final String responseBody = MAPPER.encodeToString(IdResResponse.of(List.of(expectedEid))); + given(httpClient.post(any(), any(), any(), anyLong())) + .willReturn(Future.succeededFuture(HttpClientResponse.of(200, null, responseBody))); + + given(auctionInvocationContext.auctionContext()).willReturn(auctionContext); + given(auctionContext.getActivityInfrastructure()).willReturn(activityInfrastructure); + given(activityInfrastructure.isAllowed(any(), any())).willReturn(true); + given(userFpdActivityMask.maskUser(any(), eq(false), eq(false))) + .willAnswer(invocation -> invocation.getArgument(0)); + given(userFpdActivityMask.maskDevice(any(), eq(false), eq(false))) + .willAnswer(invocation -> invocation.getArgument(0)); + + // when + final InvocationResult result = + target.call(AuctionRequestPayloadImpl.of(givenBidRequest), auctionInvocationContext).result(); + + // then + final ExtRequestPrebidData expectedData = ExtRequestPrebidData.of( + List.of("bidderGlobal"), + List.of(ExtRequestPrebidDataEidPermissions.builder() + .source("keep.com") + .bidders(singletonList("bidderGlobal")) + .build())); + + assertThat(result.status()).isEqualTo(InvocationStatus.success); + assertThat(result.payloadUpdate().apply(AuctionRequestPayloadImpl.of(givenBidRequest))) + .extracting(AuctionRequestPayload::bidRequest) + .extracting(BidRequest::getExt) + .extracting(ExtRequest::getPrebid) + .extracting(ExtRequestPrebid::getData) + .isEqualTo(expectedData); + + verify(httpClient).post( + eq("https://test.com/idres"), + argThat(headers -> headers.contains("Authorization", "Bearer auth_token", true)), + eq(MAPPER.encodeToString(givenBidRequest)), + eq(5L)); + } } diff --git a/extra/modules/optable-targeting/pom.xml b/extra/modules/optable-targeting/pom.xml index 9e6503995c0..34278806fc8 100644 --- a/extra/modules/optable-targeting/pom.xml +++ b/extra/modules/optable-targeting/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT optable-targeting diff --git a/extra/modules/ortb2-blocking/pom.xml b/extra/modules/ortb2-blocking/pom.xml index 55107ec2223..6aa8f9b2792 100644 --- a/extra/modules/ortb2-blocking/pom.xml +++ b/extra/modules/ortb2-blocking/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT ortb2-blocking diff --git a/extra/modules/pb-request-correction/pom.xml b/extra/modules/pb-request-correction/pom.xml index 30fe61d6498..e1deb0518b8 100644 --- a/extra/modules/pb-request-correction/pom.xml +++ b/extra/modules/pb-request-correction/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT pb-request-correction diff --git a/extra/modules/pb-response-correction/pom.xml b/extra/modules/pb-response-correction/pom.xml index faf24c6baba..09f08ce6d67 100644 --- a/extra/modules/pb-response-correction/pom.xml +++ b/extra/modules/pb-response-correction/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT pb-response-correction diff --git a/extra/modules/pb-richmedia-filter/pom.xml b/extra/modules/pb-richmedia-filter/pom.xml index e8db972c01e..735704921f1 100644 --- a/extra/modules/pb-richmedia-filter/pom.xml +++ b/extra/modules/pb-richmedia-filter/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT pb-richmedia-filter diff --git a/extra/modules/pb-rule-engine/pom.xml b/extra/modules/pb-rule-engine/pom.xml index 0d6d567a809..3fe12665358 100644 --- a/extra/modules/pb-rule-engine/pom.xml +++ b/extra/modules/pb-rule-engine/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT pb-rule-engine diff --git a/extra/modules/pom.xml b/extra/modules/pom.xml index e18fe0f665e..d55994fd58c 100644 --- a/extra/modules/pom.xml +++ b/extra/modules/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT ../../extra/pom.xml diff --git a/extra/modules/wurfl-devicedetection/pom.xml b/extra/modules/wurfl-devicedetection/pom.xml index 1068dc04cd4..dde02bed3ed 100644 --- a/extra/modules/wurfl-devicedetection/pom.xml +++ b/extra/modules/wurfl-devicedetection/pom.xml @@ -5,7 +5,7 @@ org.prebid.server.hooks.modules all-modules - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT wurfl-devicedetection diff --git a/extra/pom.xml b/extra/pom.xml index 836df8f4a38..2cb81b6464a 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -4,7 +4,7 @@ org.prebid prebid-server-aggregator - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT pom @@ -33,7 +33,7 @@ 10.17.0 - 3.5.5 + 3.5.10 4.5.20 2.0.1.Final 4.4 diff --git a/pom.xml b/pom.xml index b9f4a2eff8b..73513000b95 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.prebid prebid-server-aggregator - 3.39.0-SNAPSHOT + 3.40.0-SNAPSHOT extra/pom.xml diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java deleted file mode 100644 index 63ad320ca4e..00000000000 --- a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java +++ /dev/null @@ -1,358 +0,0 @@ -package org.prebid.server.bidder.adocean; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.iab.openrtb.request.App; -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.vertx.core.MultiMap; -import io.vertx.core.http.HttpMethod; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.MapUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -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; -import org.prebid.server.bidder.adocean.model.AdoceanResponseAdUnit; -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.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.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -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.3.0"; - private static final int MAX_URI_LENGTH = 8000; - private static final String MEASUREMENT_CODE_TEMPLATE = """ - """; - - 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) { - final List errors = new ArrayList<>(); - final User user = request.getUser(); - final ExtUser extUser = user != null ? user.getExt() : null; - final String consentString = extUser != null ? extUser.getConsent() : ""; - - final List> httpRequests = new ArrayList<>(); - for (Imp imp : request.getImp()) { - try { - final ExtImpAdocean extImpAdocean = parseImpExt(imp); - validateImpExt(extImpAdocean); - - final Map slaveSizes = new HashMap<>(); - slaveSizes.put(extImpAdocean.getSlaveId(), getImpSizes(imp)); - if (addRequestAndCheckIfDuplicates(httpRequests, extImpAdocean, imp.getId(), slaveSizes, - request.getTest())) { - continue; - } - httpRequests.add(createSingleRequest(request, imp, extImpAdocean, consentString, slaveSizes)); - } catch (PreBidException e) { - errors.add(BidderError.badInput(e.getMessage())); - } - } - - return Result.of(httpRequests, errors); - } - - private ExtImpAdocean parseImpExt(Imp imp) { - try { - return mapper.mapper().convertValue(imp.getExt(), ADOCEAN_EXT_TYPE_REFERENCE).getBidder(); - } catch (IllegalArgumentException e) { - throw new PreBidException( - "Error parsing adOceanExt parameters, in imp with id : " + imp.getId()); - } - } - - private static void validateImpExt(ExtImpAdocean impExt) { - if (StringUtils.isEmpty(impExt.getEmitterPrefix())) { - throw new PreBidException("No emitterPrefix param"); - } - } - - private boolean addRequestAndCheckIfDuplicates(List> httpRequests, ExtImpAdocean extImpAdocean, - String impid, Map slaveSizes, Integer test) { - for (HttpRequest request : httpRequests) { - try { - final URIBuilder uriBuilder = new URIBuilder(request.getUri()); - final List queryParams = uriBuilder.getQueryParams(); - - final String masterId = queryParams.stream() - .filter(param -> "id".equals(param.getName())) - .findFirst() - .map(NameValuePair::getValue) - .orElse(null); - - if (masterId != null && masterId.equals(extImpAdocean.getMasterId())) { - final boolean isExistingSlaveId = queryParams.stream() - .filter(param -> "aid".equals(param.getName())) - .map(param -> param.getValue().split(":")[0]) - .anyMatch(slaveId -> slaveId.equals(extImpAdocean.getSlaveId())); - if (isExistingSlaveId) { - continue; - } - - queryParams.add(new BasicNameValuePair("aid", extImpAdocean.getSlaveId() + ":" + impid)); - final List sizeValues = setSlaveSizesParam(slaveSizes, Objects.equals(test, 1)); - if (CollectionUtils.isNotEmpty(sizeValues)) { - queryParams.add(new BasicNameValuePair("aosspsizes", String.join("-", sizeValues))); - } - uriBuilder.setParameters(queryParams); - - final String url = uriBuilder.toString(); - if (url.length() < MAX_URI_LENGTH) { - final HttpRequest updatedRequest = HttpRequest.builder() - .method(HttpMethod.GET) - .uri(url) - .headers(request.getHeaders()) - .build(); - httpRequests.remove(request); - httpRequests.add(updatedRequest); - return true; - } - } - } catch (URISyntaxException e) { - throw new PreBidException(e.getMessage()); - } - } - return false; - } - - private String getImpSizes(Imp imp) { - final Banner banner = imp.getBanner(); - if (banner == null) { - return ""; - } - - final List format = banner.getFormat(); - if (CollectionUtils.isNotEmpty(format)) { - final List sizes = new ArrayList<>(); - format.forEach(singleFormat -> sizes.add( - "%sx%s".formatted(getIntOrElseZero(singleFormat.getW()), getIntOrElseZero(singleFormat.getH())))); - return String.join("_", sizes); - } - - final Integer w = banner.getW(); - final Integer h = banner.getH(); - if (w != null && h != null) { - return "%sx%s".formatted(w, h); - } - - return StringUtils.EMPTY; - } - - private int getIntOrElseZero(Integer number) { - return number != null ? number : 0; - } - - private HttpRequest createSingleRequest(BidRequest request, Imp imp, ExtImpAdocean extImpAdocean, - String consentString, Map slaveSizes) { - - return HttpRequest.builder() - .method(HttpMethod.GET) - .uri(buildUrl(imp.getId(), extImpAdocean, consentString, request, slaveSizes)) - .headers(getHeaders(request)) - .build(); - } - - private String buildUrl(String impId, ExtImpAdocean extImpAdocean, String consentString, BidRequest bidRequest, - Map slaveSizes) { - - final Integer test = bidRequest.getTest(); - final String resolvedUrl = resolveEndpointUrl(extImpAdocean, test); - - final URIBuilder uriBuilder; - try { - uriBuilder = new URIBuilder(resolvedUrl); - } catch (URISyntaxException e) { - throw new PreBidException("Invalid url: %s, error: %s".formatted(resolvedUrl, e.getMessage())); - } - - uriBuilder - .addParameter("pbsrv_v", VERSION) - .addParameter("id", extImpAdocean.getMasterId()) - .addParameter("nc", "1") - .addParameter("nosecure", "1") - .addParameter("aid", extImpAdocean.getSlaveId() + ":" + impId); - - if (StringUtils.isNotEmpty(consentString)) { - uriBuilder.addParameter("gdpr_consent", consentString); - uriBuilder.addParameter("gdpr", "1"); - } - - final User user = bidRequest.getUser(); - if (user != null && StringUtils.isNotEmpty(user.getBuyeruid())) { - uriBuilder.addParameter("hcuserid", user.getBuyeruid()); - } - - final App app = bidRequest.getApp(); - if (app != null) { - uriBuilder.addParameter("app", "1"); - uriBuilder.addParameter("appname", app.getName()); - uriBuilder.addParameter("appbundle", app.getBundle()); - uriBuilder.addParameter("appdomain", app.getDomain()); - } - - final Device device = bidRequest.getDevice(); - if (device != null) { - if (StringUtils.isNotEmpty(device.getIfa())) { - uriBuilder.addParameter("ifa", device.getIfa()); - } else { - uriBuilder.addParameter("dpidmd5", device.getDpidmd5()); - } - - uriBuilder.addParameter("devos", device.getOs()); - uriBuilder.addParameter("devosv", device.getOsv()); - uriBuilder.addParameter("devmodel", device.getModel()); - uriBuilder.addParameter("devmake", device.getMake()); - } - - final List sizeValues = setSlaveSizesParam(slaveSizes, Objects.equals(test, 1)); - - if (CollectionUtils.isNotEmpty(sizeValues)) { - uriBuilder.addParameter("aosspsizes", String.join("-", sizeValues)); - } - - return uriBuilder.toString(); - } - - private String resolveEndpointUrl(ExtImpAdocean extImpAdocean, Integer test) { - final String url = endpointUrl.replace("{{Host}}", extImpAdocean.getEmitterPrefix()); - final int randomizedPart = Objects.equals(test, 1) ? 10000000 : 10000000 + (int) (Math.random() * 89999999); - return "%s/_%s/ad.json".formatted(url, randomizedPart); - } - - private List setSlaveSizesParam(Map slaveSizes, boolean orderByKey) { - final Set slaveIDs = orderByKey ? new TreeSet<>(slaveSizes.keySet()) : slaveSizes.keySet(); - - return slaveIDs.stream() - .filter(slaveId -> StringUtils.isNotEmpty(slaveSizes.get(slaveId))) - .map(rawSlaveID -> "%s~%s".formatted( - rawSlaveID.replaceFirst("adocean", ""), - slaveSizes.get(rawSlaveID))) - .toList(); - } - - private static MultiMap getHeaders(BidRequest request) { - final MultiMap headers = HttpUtil.headers(); - - final Device device = request.getDevice(); - if (device != null) { - HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER, device.getUa()); - HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, device.getIpv6()); - HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, device.getIp()); - } - - final Site site = request.getSite(); - if (site != null) { - HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, site.getPage()); - } - - return headers; - } - - @Override - public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { - final List params; - try { - params = URLEncodedUtils.parse(new URI(httpCall.getRequest().getUri()), StandardCharsets.UTF_8); - } catch (URISyntaxException e) { - return Result.withError(BidderError.badInput(e.getMessage())); - } - - final Map auctionIds = params != null ? params.stream() - .filter(param -> "aid".equals(param.getName())) - .map(param -> param.getValue().split(":")) - .collect(Collectors.toMap(name -> name[0], value -> value[1])) : null; - - final List adoceanResponses; - try { - adoceanResponses = getAdoceanResponseAdUnitList(httpCall.getResponse().getBody()); - } catch (PreBidException e) { - return Result.withError(BidderError - .badServerResponse("Failed to decode: No content to map due to end-of-input")); - } - - final List bidderBids = adoceanResponses.stream() - .filter(adoceanResponse -> !"true".equals(adoceanResponse.getError())) - .filter(adoceanResponse -> - StringUtils.isNotBlank(MapUtils.getString(auctionIds, adoceanResponse.getId()))) - .map(adoceanResponse -> BidderBid.of(createBid(auctionIds, adoceanResponse), BidType.banner, - adoceanResponse.getCurrency())) - .toList(); - - return Result.withValues(bidderBids); - } - - private static Bid createBid(Map auctionIds, AdoceanResponseAdUnit adoceanResponse) { - final String adm = MEASUREMENT_CODE_TEMPLATE - .formatted(adoceanResponse.getWinUrl(), adoceanResponse.getStatsUrl()) - + HttpUtil.decodeUrl(adoceanResponse.getCode()); - final String bidPrice = adoceanResponse.getPrice(); - - return Bid.builder() - .id(adoceanResponse.getId()) - .impid(auctionIds.get(adoceanResponse.getId())) - .adm(adm) - .price(NumberUtils.isParsable(bidPrice) ? new BigDecimal(bidPrice) : BigDecimal.ZERO) - .w(NumberUtils.toInt(adoceanResponse.getWidth(), 0)) - .h(NumberUtils.toInt(adoceanResponse.getHeight(), 0)) - .crid(adoceanResponse.getCrid()) - .build(); - } - - private List getAdoceanResponseAdUnitList(String responseBody) { - try { - return mapper.mapper().readValue( - responseBody, - mapper.mapper().getTypeFactory().constructCollectionType(List.class, AdoceanResponseAdUnit.class)); - } catch (IOException e) { - throw new PreBidException(e.getMessage()); - } - } -} 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 deleted file mode 100644 index b2bd0142cee..00000000000 --- a/src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java +++ /dev/null @@ -1,32 +0,0 @@ -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/bidder/clydo/ClydoBidder.java b/src/main/java/org/prebid/server/bidder/clydo/ClydoBidder.java new file mode 100644 index 00000000000..ca7b04ee32f --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/clydo/ClydoBidder.java @@ -0,0 +1,182 @@ +package org.prebid.server.bidder.clydo; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Device; +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.netty.handler.codec.http.HttpHeaderValues; +import io.vertx.core.MultiMap; +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.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.clydo.ExtImpClydo; +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.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class ClydoBidder implements Bidder { + + private static final TypeReference> CLYDO_EXT_TYPE_REFERENCE = + new TypeReference<>() { + }; + + private static final String REGION_MACRO = "{{Region}}"; + private static final String PARTNER_ID_MACRO = "{{PartnerId}}"; + private static final String DEFAULT_REGION = "us"; + private static final String X_OPENRTB_VERSION = "2.5"; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public ClydoBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + final List errors = new ArrayList<>(); + final List> httpRequests = new ArrayList<>(); + + for (Imp imp : request.getImp()) { + try { + final ExtImpClydo extImpClydo = parseExtImp(imp); + final HttpRequest httpRequest = makeHttpRequest(request, imp, extImpClydo); + httpRequests.add(httpRequest); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + } + } + + if (httpRequests.isEmpty()) { + return Result.withError(BidderError.badInput("found no valid impressions")); + } + + return Result.of(httpRequests, errors); + } + + private ExtImpClydo parseExtImp(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), CLYDO_EXT_TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException("Cannot deserialize ExtImpClydo: " + e.getMessage()); + } + } + + private HttpRequest makeHttpRequest(BidRequest request, Imp imp, ExtImpClydo extImpClydo) { + final BidRequest outgoingRequest = request.toBuilder().imp(List.of(imp)).build(); + + return BidderUtil.defaultRequest( + outgoingRequest, + constructHeaders(request), + resolveUrl(endpointUrl, extImpClydo), mapper); + } + + private static MultiMap constructHeaders(BidRequest bidRequest) { + final Device device = bidRequest.getDevice(); + final MultiMap headers = HttpUtil.headers(); + + headers.set(HttpUtil.X_OPENRTB_VERSION_HEADER, X_OPENRTB_VERSION); + headers.set(HttpUtil.ACCEPT_HEADER, HttpHeaderValues.APPLICATION_JSON); + headers.set(HttpUtil.CONTENT_TYPE_HEADER, HttpUtil.APPLICATION_JSON_CONTENT_TYPE); + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, + ObjectUtil.getIfNotNull(device, Device::getIpv6)); + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, + ObjectUtil.getIfNotNull(device, Device::getIp)); + HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER, + ObjectUtil.getIfNotNull(device, Device::getUa)); + + return headers; + } + + private static String resolveUrl(String endpoint, ExtImpClydo extImp) { + return endpoint + .replace(REGION_MACRO, getRegionInfo(extImp)) + .replace(PARTNER_ID_MACRO, HttpUtil.encodeUrl(extImp.getPartnerId())); + } + + private static String getRegionInfo(ExtImpClydo extImp) { + final String region = extImp.getRegion(); + + return switch (region) { + case "us", "usw", "eu", "apac" -> region; + case null, default -> DEFAULT_REGION; + }; + } + + @Override + public Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + final List bidderBids = extractBids(bidRequest, bidResponse); + return Result.withValues(bidderBids); + } catch (DecodeException | PreBidException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private List extractBids(BidRequest bidRequest, BidResponse bidResponse) { + if (bidResponse == null || bidResponse.getSeatbid() == null || bidResponse.getSeatbid().isEmpty()) { + return Collections.emptyList(); + } + + final Map impIdToBidTypeMap = buildImpIdToBidTypeMap(bidRequest); + + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(List::stream) + .filter(Objects::nonNull) + .map(bid -> BidderBid.of(bid, resolveBidType(bid, impIdToBidTypeMap), bidResponse.getCur())) + .collect(Collectors.toList()); + } + + private static Map buildImpIdToBidTypeMap(BidRequest bidRequest) { + final Map impIdToBidTypeMap = new HashMap<>(); + for (Imp imp : bidRequest.getImp()) { + final String impId = imp.getId(); + final BidType bidType = determineBidType(imp); + impIdToBidTypeMap.put(impId, bidType); + } + + return impIdToBidTypeMap; + } + + private static BidType determineBidType(Imp imp) { + if (imp.getAudio() != null) { + return BidType.audio; + } else if (imp.getVideo() != null) { + return BidType.video; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } else if (imp.getBanner() != null) { + return BidType.banner; + } + + throw new PreBidException("Failed to get media type"); + } + + private static BidType resolveBidType(Bid bid, Map impIdToBidTypeMap) { + return impIdToBidTypeMap.getOrDefault(bid.getImpid(), BidType.banner); + } +} diff --git a/src/main/java/org/prebid/server/bidder/nexx360/Nexx360Bidder.java b/src/main/java/org/prebid/server/bidder/nexx360/Nexx360Bidder.java index 180f545bf47..b4b643cc3eb 100644 --- a/src/main/java/org/prebid/server/bidder/nexx360/Nexx360Bidder.java +++ b/src/main/java/org/prebid/server/bidder/nexx360/Nexx360Bidder.java @@ -1,6 +1,7 @@ package org.prebid.server.bidder.nexx360; 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.response.Bid; @@ -81,7 +82,9 @@ private ExtImpNexx360 parseImpExt(Imp imp) { private Imp modifyImp(Imp imp) { return imp.toBuilder() - .ext(mapper.mapper().createObjectNode().set(BIDDER_NAME, imp.getExt().get("bidder"))) + .ext(imp.getExt().deepCopy() + .without("bidder") + .set(BIDDER_NAME, imp.getExt().get("bidder"))) .build(); } diff --git a/src/main/java/org/prebid/server/bidder/teal/TealBidder.java b/src/main/java/org/prebid/server/bidder/teal/TealBidder.java new file mode 100644 index 00000000000..c1869421dd8 --- /dev/null +++ b/src/main/java/org/prebid/server/bidder/teal/TealBidder.java @@ -0,0 +1,200 @@ +package org.prebid.server.bidder.teal; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.iab.openrtb.request.App; +import com.iab.openrtb.request.BidRequest; +import com.iab.openrtb.request.Imp; +import com.iab.openrtb.request.Publisher; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +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.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.ExtRequest; +import org.prebid.server.proto.openrtb.ext.request.teal.ExtImpTeal; +import org.prebid.server.proto.openrtb.ext.response.BidType; +import org.prebid.server.util.BidderUtil; +import org.prebid.server.util.HttpUtil; + +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 TealBidder implements Bidder { + + private static final TypeReference> TYPE_REFERENCE = new TypeReference<>() { + }; + + private final String endpointUrl; + private final JacksonMapper mapper; + + public TealBidder(String endpointUrl, JacksonMapper mapper) { + this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.mapper = Objects.requireNonNull(mapper); + } + + @Override + public Result>> makeHttpRequests(BidRequest request) { + final List modifiedImps = new ArrayList<>(); + final List errors = new ArrayList<>(); + String account = null; + + for (Imp imp : request.getImp()) { + final ExtImpTeal extImpTeal; + try { + extImpTeal = parseImpExt(imp); + validateImpExt(extImpTeal); + } catch (PreBidException e) { + errors.add(BidderError.badInput(e.getMessage())); + continue; + } + + account = account == null ? extImpTeal.getAccount() : account; + modifiedImps.add(modifyImp(imp, extImpTeal.getPlacement())); + } + + if (modifiedImps.isEmpty()) { + return Result.withErrors(errors); + } + + final BidRequest modifiedRequest = modifyBidRequest(request, account, modifiedImps); + return Result.of( + Collections.singletonList(BidderUtil.defaultRequest(modifiedRequest, endpointUrl, mapper)), + errors); + } + + private ExtImpTeal parseImpExt(Imp imp) { + try { + return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder(); + } catch (IllegalArgumentException e) { + throw new PreBidException("Error parsing imp.ext for impression " + imp.getId()); + } + } + + private static void validateImpExt(ExtImpTeal extImpTeal) { + if (StringUtils.isBlank(extImpTeal.getAccount())) { + throw new PreBidException("account parameter failed validation"); + } + + final String placement = extImpTeal.getPlacement(); + if (placement != null && StringUtils.isBlank(placement)) { + throw new PreBidException("placement parameter failed validation"); + } + } + + private static Imp modifyImp(Imp imp, String placement) { + if (placement == null) { + return imp; + } + + final ObjectNode modifiedExt = imp.getExt().deepCopy(); + getOrCreate(getOrCreate(modifiedExt, "prebid"), "storedrequest") + .put("id", placement); + + return imp.toBuilder().ext(modifiedExt).build(); + } + + private static ObjectNode getOrCreate(ObjectNode parent, String field) { + final JsonNode child = parent.get(field); + return child != null && child.isObject() + ? (ObjectNode) child + : parent.putObject(field); + } + + private BidRequest modifyBidRequest(BidRequest request, String account, List modifiedImps) { + final ExtRequest ext = ObjectUtils.defaultIfNull(request.getExt(), ExtRequest.empty()); + ext.addProperty("bids", mapper.mapper().createObjectNode().put("pbs", 1)); + + return request.toBuilder() + .site(modifySite(request.getSite(), account)) + .app(modifyApp(request.getApp(), account)) + .imp(modifiedImps) + .ext(ext) + .build(); + } + + private static Site modifySite(Site site, String account) { + return site != null + ? site.toBuilder() + .publisher(modifyPublisher(site.getPublisher(), account)) + .build() + : null; + } + + private static App modifyApp(App app, String account) { + return app != null + ? app.toBuilder() + .publisher(modifyPublisher(app.getPublisher(), account)) + .build() + : null; + } + + private static Publisher modifyPublisher(Publisher publisher, String account) { + return Optional.ofNullable(publisher) + .map(Publisher::toBuilder) + .orElseGet(Publisher::builder) + .id(account) + .build(); + } + + @Override + public final Result> makeBids(BidderCall httpCall, BidRequest bidRequest) { + try { + final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class); + return Result.withValues(extractBids(httpCall.getRequest().getPayload(), bidResponse)); + } catch (DecodeException e) { + return Result.withError(BidderError.badServerResponse(e.getMessage())); + } + } + + private static List extractBids(BidRequest bidRequest, BidResponse bidResponse) { + if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) { + return Collections.emptyList(); + } + return bidsFromResponse(bidRequest, bidResponse); + } + + private static List bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) { + return bidResponse.getSeatbid().stream() + .filter(Objects::nonNull) + .map(SeatBid::getBid) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur())) + .toList(); + } + + private static BidType getBidType(String impId, List imps) { + 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.getAudio() != null) { + return BidType.audio; + } else if (imp.getXNative() != null) { + return BidType.xNative; + } + } + } + return BidType.banner; + } +} 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 deleted file mode 100644 index aa364d222ae..00000000000 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.prebid.server.proto.openrtb.ext.request.adocean; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Value; - -/** - * Defines the contract for bidrequest.imp[i].ext.adocean - */ -@Value(staticConstructor = "of") -public class ExtImpAdocean { - - @JsonProperty("emitterPrefix") - String emitterPrefix; - - @JsonProperty("masterId") - String masterId; - - @JsonProperty("slaveId") - String slaveId; -} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/clydo/ExtImpClydo.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/clydo/ExtImpClydo.java new file mode 100644 index 00000000000..68338055de4 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/clydo/ExtImpClydo.java @@ -0,0 +1,13 @@ +package org.prebid.server.proto.openrtb.ext.request.clydo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtImpClydo { + + @JsonProperty("partnerId") + String partnerId; + + String region; +} diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/teal/ExtImpTeal.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/teal/ExtImpTeal.java new file mode 100644 index 00000000000..e52ec990e84 --- /dev/null +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/teal/ExtImpTeal.java @@ -0,0 +1,11 @@ +package org.prebid.server.proto.openrtb.ext.request.teal; + +import lombok.Value; + +@Value(staticConstructor = "of") +public class ExtImpTeal { + + String account; + + String placement; +} diff --git a/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java similarity index 59% rename from src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java rename to src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java index d16984e3c5e..98cedfc8d08 100644 --- a/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java @@ -1,7 +1,7 @@ package org.prebid.server.spring.config.bidder; import org.prebid.server.bidder.BidderDeps; -import org.prebid.server.bidder.adocean.AdoceanBidder; +import org.prebid.server.bidder.clydo.ClydoBidder; import org.prebid.server.json.JacksonMapper; import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties; import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler; @@ -16,26 +16,26 @@ import jakarta.validation.constraints.NotBlank; @Configuration -@PropertySource(value = "classpath:/bidder-config/adocean.yaml", factory = YamlPropertySourceFactory.class) -public class AdoceanConfiguration { +@PropertySource(value = "classpath:/bidder-config/clydo.yaml", factory = YamlPropertySourceFactory.class) +public class ClydoConfiguration { - private static final String BIDDER_NAME = "adocean"; + private static final String BIDDER_NAME = "clydo"; - @Bean("adoceanConfigurationProperties") - @ConfigurationProperties("adapters.adocean") + @Bean("clydoConfigurationProperties") + @ConfigurationProperties("adapters.clydo") BidderConfigurationProperties configurationProperties() { return new BidderConfigurationProperties(); } @Bean - BidderDeps adoceanBidderDeps(BidderConfigurationProperties adoceanConfigurationProperties, - @NotBlank @Value("${external-url}") String externalUrl, - JacksonMapper mapper) { + BidderDeps clydoBidderDeps(BidderConfigurationProperties clydoConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + JacksonMapper mapper) { return BidderDepsAssembler.forBidder(BIDDER_NAME) - .withConfig(adoceanConfigurationProperties) + .withConfig(clydoConfigurationProperties) .usersyncerCreator(UsersyncerCreator.create(externalUrl)) - .bidderCreator(config -> new AdoceanBidder(config.getEndpoint(), mapper)) + .bidderCreator(config -> new ClydoBidder(config.getEndpoint(), mapper)) .assemble(); } } diff --git a/src/main/java/org/prebid/server/spring/config/bidder/TealConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/TealConfiguration.java new file mode 100644 index 00000000000..cbbff678a7e --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/TealConfiguration.java @@ -0,0 +1,42 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +import org.prebid.server.bidder.teal.TealBidder; +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/teal.yaml", factory = YamlPropertySourceFactory.class) +public class TealConfiguration { + + private static final String BIDDER_NAME = "teal"; + + @Bean("tealConfigurationProperties") + @ConfigurationProperties("adapters.teal") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps tealBidderDeps(BidderConfigurationProperties tealConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + JacksonMapper mapper) { + + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(tealConfigurationProperties) + .usersyncerCreator(UsersyncerCreator.create(externalUrl)) + .bidderCreator(config -> new TealBidder(config.getEndpoint(), mapper)) + .assemble(); + } + +} diff --git a/src/main/resources/bidder-config/adocean.yaml b/src/main/resources/bidder-config/adocean.yaml deleted file mode 100644 index f659947c985..00000000000 --- a/src/main/resources/bidder-config/adocean.yaml +++ /dev/null @@ -1,11 +0,0 @@ -adapters: - adocean: - endpoint: https://{{Host}}.adocean.pl - meta-info: - maintainer-email: aoteam@gemius.com - app-media-types: - - banner - site-media-types: - - banner - supported-vendors: - vendor-id: 328 diff --git a/src/main/resources/bidder-config/adverxo.yaml b/src/main/resources/bidder-config/adverxo.yaml index d2fd18f505b..b363a70bc08 100644 --- a/src/main/resources/bidder-config/adverxo.yaml +++ b/src/main/resources/bidder-config/adverxo.yaml @@ -31,20 +31,6 @@ adapters: url: https://taetee.com/usync?type=image&gdpr={{gdpr}}&consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}} uid-macro: '$UID' support-cors: false - mobupps: - enabled: false - endpoint: https://mobupps.pbsadverxo.com/auction?id={{adUnitId}}&auth={{auth}} - usersync: - enabled: false - cookie-family-name: mobupps - iframe: - url: https://mobupps.pbsadverxo.com/usync?type=iframe&gdpr={{gdpr}}&consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}} - uid-macro: '$UID' - support-cors: false - redirect: - url: https://mobupps.pbsadverxo.com/usync?type=image&gdpr={{gdpr}}&consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}} - uid-macro: '$UID' - support-cors: false meta-info: maintainer-email: developer@adverxo.com app-media-types: diff --git a/src/main/resources/bidder-config/aso.yaml b/src/main/resources/bidder-config/aso.yaml index fa6a6381741..5f3bdad4829 100644 --- a/src/main/resources/bidder-config/aso.yaml +++ b/src/main/resources/bidder-config/aso.yaml @@ -7,16 +7,34 @@ adapters: endpoint: https://srv.datacygnal.io/pbs/bidder?zid={{ZoneID}} meta-info: maintainer-email: contact@bcm.ltd + usersync: + cookie-family-name: bcmint + redirect: + url: https://track.datacygnal.io/sync/v2?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&usp={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '{uid}' bidagency: enabled: false endpoint: https://srv.bidgx.com/pbs/bidder?zid={{ZoneID}} meta-info: maintainer-email: aso@bidgency.com + usersync: + cookie-family-name: bidagency + redirect: + url: https://track.bidgx.com/sync/v2?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&usp={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '{uid}' kuantyx: enabled: false endpoint: https://srv.kntxy.com/pbs/bidder?zid={{ZoneID}} meta-info: maintainer-email: ssp@kuantyx.com + usersync: + cookie-family-name: kuantyx + redirect: + url: https://track.kntxy.com/sync/v2?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&usp={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '{uid}' meta-info: maintainer-email: support@adsrv.org app-media-types: @@ -28,3 +46,9 @@ adapters: - video - native vendor-id: 0 + usersync: + cookie-family-name: aso + redirect: + url: https://track.aso1.net/sync/v2?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&usp={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '{uid}' diff --git a/src/main/resources/bidder-config/clydo.yaml b/src/main/resources/bidder-config/clydo.yaml new file mode 100644 index 00000000000..aa90e3a4f07 --- /dev/null +++ b/src/main/resources/bidder-config/clydo.yaml @@ -0,0 +1,21 @@ +adapters: + clydo: + endpoint: http://region={{Region}}.clydo.io/partnerId={{PartnerId}} + modifying-vast-xml-allowed: true + endpoint-compression: gzip + geoscope: + - global + meta-info: + maintainer-email: cto@clydo.io + app-media-types: + - banner + - video + - audio + - native + site-media-types: + - banner + - video + - audio + - native + supported-vendors: + vendor-id: 0 diff --git a/src/main/resources/bidder-config/nexx360.yaml b/src/main/resources/bidder-config/nexx360.yaml index 443ca7f5ab4..975dcd6a21e 100644 --- a/src/main/resources/bidder-config/nexx360.yaml +++ b/src/main/resources/bidder-config/nexx360.yaml @@ -19,4 +19,4 @@ adapters: - native - audio supported-vendors: - vendor-id: 0 + vendor-id: 965 diff --git a/src/main/resources/bidder-config/smarthub.yaml b/src/main/resources/bidder-config/smarthub.yaml index 73a50212676..e3d370266b5 100644 --- a/src/main/resources/bidder-config/smarthub.yaml +++ b/src/main/resources/bidder-config/smarthub.yaml @@ -11,9 +11,6 @@ adapters: tredio: enabled: false endpoint: https://tredio-prebid.attekmi.com/pbserver/?seat={{AccountID}}&token={{SourceId}} - vimayx: - enabled: false - endpoint: https://vimayx-prebid.attekmi.com/pbserver/?seat={{AccountID}}&token={{SourceId}} felixads: enabled: false endpoint: https://felixads-prebid.attekmi.com/pbserver/?seat={{AccountID}}&token={{SourceId}} @@ -29,6 +26,9 @@ adapters: addigi: enabled: false endpoint: https://addigi-prebid.attekmi.com/pbserver/?seat={{AccountID}}&token={{SourceId}} + radianfusion: + enabled: false + endpoint: https://radiantfusion-prebid.attekmi.co/pbserver/?seat={{AccountID}}&token={{SourceId}} meta-info: maintainer-email: prebid@attekmi.com app-media-types: diff --git a/src/main/resources/bidder-config/teal.yaml b/src/main/resources/bidder-config/teal.yaml new file mode 100644 index 00000000000..00d1a6d48fc --- /dev/null +++ b/src/main/resources/bidder-config/teal.yaml @@ -0,0 +1,30 @@ +adapters: + teal: + ortb-version: "2.6" + endpoint: https://a.bids.ws/openrtb2/auction + modifying-vast-xml-allowed: true + endpoint-compression: gzip + geoscope: + - global + aliases: + tealplus: + enabled: false + ortb: + multiformat-supported: true + meta-info: + maintainer-email: prebid@teal.works + app-media-types: + - banner + - video + - native + site-media-types: + - banner + - video + - native + supported-vendors: + vendor-id: 1378 + usersync: + cookie-family-name: teal + iframe: + url: https://bids.ws/load-pbs.html?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect_url={{redirect_url}} + support-cors: false diff --git a/src/main/resources/bidder-config/teqblaze.yaml b/src/main/resources/bidder-config/teqblaze.yaml index 5e1006433ae..a7647ad62f3 100644 --- a/src/main/resources/bidder-config/teqblaze.yaml +++ b/src/main/resources/bidder-config/teqblaze.yaml @@ -2,6 +2,21 @@ adapters: teqblaze: endpoint: http:// aliases: + 360playvid: + enabled: false + endpoint: https://ssp.360playvid.com/pserver + meta-info: + maintainer-email: prebid@360playvid.com + usersync: + enabled: true + redirect: + url: https://cookie.360playvid.com/pbserver?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&coppa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '[UID]' + iframe: + url: https://cookie.360playvid.com/pbserverIframe?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ccpa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&pbserverUrl={{redirect_url}} + uid-macro: '[UID]' + support-cors: false pinkLion: enabled: false endpoint: https://us-east-ep.pinklion.io/pserver @@ -43,6 +58,23 @@ adapters: mata-info: maintainer-email: support@gravite.net vendor-id: 377 + progx: + enabled: false + endpoint: https://us-east.progrtb.com/pserver + meta-info: + maintainer-email: pxteam@programmaticx.ai + vendor-id: 1344 + usersync: + enabled: true + cookie-family-name: progx + iframe: + url: https://sync.progrtb.com/pbserverIframe?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ccpa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&pbserverUrl={{.RedirectURL}} + support-cors: false + uid-macro: '[UID]' + redirect: + url: https://sync.progrtb.com/pbserver?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ccpa={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}} + support-cors: false + uid-macro: '[UID]' meta-info: maintainer-email: github@teqblaze.com app-media-types: diff --git a/src/main/resources/bidder-config/vidazoo.yaml b/src/main/resources/bidder-config/vidazoo.yaml index 3e1768fb0cb..adb9b9758a5 100644 --- a/src/main/resources/bidder-config/vidazoo.yaml +++ b/src/main/resources/bidder-config/vidazoo.yaml @@ -2,19 +2,6 @@ adapters: vidazoo: endpoint: https://prebidsrvr.cootlogix.com/openrtb/ aliases: - progx: - enabled: false - endpoint: https://exchange.programmaticx.ai/openrtb/ - meta-info: - maintainer-email: pxteam@programmaticx.ai - vendor-id: 1344 - usersync: - enabled: true - cookie-family-name: progx - iframe: - url: https://sync.programmaticx.ai/api/user/html/685297194d85991a5e6e36dd?pbs=true&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}}&gpp={{gpp}}&gpp_sid={{gpp_sid}} - support-cors: false - uid-macro: '${userId}' omnidex: endpoint: https://exchange.omni-dex.io/openrtb/ usersync: diff --git a/src/main/resources/static/bidder-params/adocean.json b/src/main/resources/static/bidder-params/adocean.json deleted file mode 100644 index 5c62c410fb1..00000000000 --- a/src/main/resources/static/bidder-params/adocean.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$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": "Deprecated, use emitterPrefix instead. AdOcean emiter", - "pattern": ".+" - }, - "emitterPrefix": { - "type": "string", - "description": "AdOcean emitter prefix", - "pattern": "^[\\w\\-]+$" - }, - "masterId": { - "type": "string", - "description": "Master's id", - "pattern": "^[\\w.]+$" - }, - "slaveId": { - "type": "string", - "description": "Slave's id", - "pattern": "^adocean[\\w.]+$" - } - }, - "oneOf": [ - { - "required": [ - "emiter", - "masterId", - "slaveId" - ] - }, - { - "required": [ - "emitterPrefix", - "masterId", - "slaveId" - ] - } - ] -} diff --git a/src/main/resources/static/bidder-params/adot.json b/src/main/resources/static/bidder-params/adot.json index e82b17be8d4..45caca4da80 100644 --- a/src/main/resources/static/bidder-params/adot.json +++ b/src/main/resources/static/bidder-params/adot.json @@ -14,7 +14,8 @@ }, "publisherPath": { "type": "string", - "description": "An optional path used in the bid endpoint" + "enum": ["/hubvisor", "/spotx"], + "description": "An optional path used in the bid endpoint. Only '/hubvisor' and '/spotx' are allowed." } }, "required": [] diff --git a/src/main/resources/static/bidder-params/clydo.json b/src/main/resources/static/bidder-params/clydo.json new file mode 100644 index 00000000000..71b60d09405 --- /dev/null +++ b/src/main/resources/static/bidder-params/clydo.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Clydo Adapter Params", + "description": "A schema which validates params accepted by the Clydo adapter", + "type": "object", + "properties": { + "partnerId": { + "type": "string", + "description": "Partner ID", + "minLength": 1 + }, + "region": { + "type": "string", + "description": "Regional endpoint identifier (us, usw, eu, apac)", + "enum": ["us", "usw", "eu", "apac"] + } + }, + + "required": ["partnerId"] +} diff --git a/src/main/resources/static/bidder-params/teal.json b/src/main/resources/static/bidder-params/teal.json new file mode 100644 index 00000000000..52e073c3af4 --- /dev/null +++ b/src/main/resources/static/bidder-params/teal.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Teal Adapter Params", + "description": "A schema which validates params accepted by the Teal adapter", + "type": "object", + "properties": { + "account": { + "type": "string", + "description": "Account ID" + }, + "placement": { + "type": "string", + "description": "Placement ID or name (optional)" + } + }, + "required": [ + "account" + ] +} diff --git a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java b/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java deleted file mode 100644 index 9657dd88767..00000000000 --- a/src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java +++ /dev/null @@ -1,572 +0,0 @@ -package org.prebid.server.bidder.adocean; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.iab.openrtb.request.App; -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.jupiter.api.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.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.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.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import static java.util.Arrays.asList; -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 org.prebid.server.bidder.model.BidderError.Type.bad_server_response; - -public class AdoceanBidderTest extends VertxTest { - - private static final String ENDPOINT_URL = "https://{{Host}}"; - - private final AdoceanBidder target = new AdoceanBidder(ENDPOINT_URL, jacksonMapper); - - @Test - public void creationShouldFailOnInvalidEndpointUrl() { - assertThatIllegalArgumentException().isThrownBy(() -> new AdoceanBidder("invalid_url", jacksonMapper)); - } - - @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 = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()) - .containsExactly(BidderError.badInput("Error parsing adOceanExt parameters, in imp with id : 123")); - } - - @Test - public void makeHttpRequestsShouldReturnErrorIfEndpointUrlComposingFails() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("invalid domain", "masterId", - "adoceanmyaozpniqismex")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).hasSize(1) - .allSatisfy(error -> { - assertThat(error.getMessage()).startsWith("Invalid url: https://invalid domain/"); - assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); - }); - } - - @Test - public void makeHttpRequestsShouldReturnErrorIfExtImpEmitterPrefixIsEmpty() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("", "masterId", "adoceanmyaozpniqismex")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).isEmpty(); - assertThat(result.getErrors()).containsExactly(BidderError.badInput("No emitterPrefix param")); - } - - @Test - public void makeHttpRequestsShouldReturnErrorIfExtImpEmitterPrefixIsNotSupplied() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of(null, "masterId", "adoceanmyaozpniqismex")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).isEmpty(); - assertThat(result.getErrors()).containsExactly(BidderError.badInput("No emitterPrefix param")); - } - - @Test - public void makeHttpRequestsShouldCreateRequestForEveryValidImp() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .buyeruid("testBuyerUid") - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(asList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().format(asList(Format.builder().h(250).w(300).build(), - Format.builder().h(320).w(600).build())).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "masterId", - "adoceanmyaozpniqismex")))).build(), - Imp.builder() - .id("notValidImp") - .ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode()))).build(), - Imp.builder() - .id("i2-test") - .banner(Banner.builder().w(577).h(333).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("em.dom", "masterId2", - "slaveId")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()) - .containsExactly(BidderError.badInput("Error parsing adOceanExt parameters, " - + "in imp with id : notValidImp")); - assertThat(result.getValue()).hasSize(2) - .extracting(HttpRequest::getUri) - .containsExactly("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0&id=masterId&nc=1" - + "&nosecure=1&aid=adoceanmyaozpniqismex%3Aao-test&gdpr_consent=consent&gdpr=1" - + "&hcuserid=testBuyerUid&aosspsizes=myaozpniqismex" - + "%7E300x250_600x320", "https://em.dom/_10000000/ad.json?pbsrv_v=1.3.0&id=" - + "masterId2&nc=1&nosecure=1&aid=slaveId%3Ai2-test&gdpr_consent=consent&gdpr=1" - + "&hcuserid=testBuyerUid&aosspsizes=slaveId%7E577x333"); - } - - @Test - public void makeHttpRequestsShouldCreateUniqueRequestIfMasterIdEqualsAndSlaveIdExists() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .buyeruid("testBuyerUid") - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(asList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().format(asList(Format.builder().h(250).w(300).build(), - Format.builder().h(320).w(600).build())).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "masterId", - "slaveId")))).build(), - Imp.builder() - .id("i2-test") - .banner(Banner.builder().w(577).h(333).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("em.dom", "masterId", - "slaveId")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).hasSize(2); - } - - @Test - public void makeHttpRequestsShouldCreateRequestWithoutSizeIfBannerSizesNotPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "masterId", - "adoceanmyaozpniqismex")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()) - .extracting(HttpRequest::getUri) - .containsExactly("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0&id=masterId&nc=1&nosecure=1" - + "&aid=adoceanmyaozpniqismex%3Aao-test&gdpr_consent=consent&gdpr=1"); - } - - @Test - public void makeHttpRequestsShouldUpdateRequestsForSimilarSlaveIds() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .buyeruid("testBuyerUid") - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(asList(Imp.builder() - .id("ao-test") - .banner(Banner.builder().format(asList(Format.builder().h(250).w(300).build(), - Format.builder().h(320).w(600).build())).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "masterId", - "slaveId")))).build(), - Imp.builder() - .id("i2-test") - .banner(Banner.builder().w(577).h(333).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("em.dom", "masterId", - "slaveId2")))).build())) - .test(1) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).hasSize(1) - .extracting(HttpRequest::getUri) - .containsExactlyInAnyOrder("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0&id=masterId&nc=1" - + "&nosecure=1&aid=slaveId%3Aao-test&gdpr_consent=consent&gdpr=1&hcuserid=testBuyerUid" - + "&aosspsizes=slaveId%7E300x250_600x320&aid=slaveId2%3Ai2-test&aosspsizes=slaveId2%7E577x333"); - } - - @Test - public void makeHttpRequestsShouldSetExpectedHeadersIfDeviceIpIsPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .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())) - .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 = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue().getFirst().getHeaders()).isNotNull() - .extracting(Map.Entry::getKey, Map.Entry::getValue) - .containsExactly(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(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .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 = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue().getFirst().getHeaders()).isNotNull() - .extracting(Map.Entry::getKey, Map.Entry::getValue) - .containsExactly(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 makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() throws JsonProcessingException { - // given - final BidderCall httpCall = givenHttpCall(null, ""); - - // when - final Result> result = target.makeBids(httpCall, null); - - // then - assertThat(result.getErrors()).hasSize(1) - .allSatisfy(error -> { - assertThat(error.getType()).isEqualTo(bad_server_response); - assertThat(error.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(adoceanResponseCreator(identity()), - adoceanResponseCreator(response -> response.id("adoceanmyaozpniqis"))); - - final BidderCall httpCall = givenHttpCall(null, mapper.writeValueAsString(adoceanResponseAdUnit)); - - // when - final Result> result = target.makeBids(httpCall, bidRequest); - - // then - final String adm = """ - \s""".formatted("https://win-url.com", "https://stats-url.com"); - - 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.getValue().getFirst().getBid().getAdm()).isEqualTo(adm); - assertThat(result.getErrors()).isEmpty(); - 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( - adoceanResponseCreator(response -> response.error("true")), - adoceanResponseCreator(response -> response.id("adoceanmyaozpniqis"))); - - final BidderCall httpCall = givenHttpCall(null, mapper.writeValueAsString(adoceanResponseAdUnit)); - - // when - final Result> result = target.makeBids(httpCall, bidRequest); - - // then - assertThat(result.getErrors()).isEmpty(); - assertThat(result.getValue()).isEqualTo(Collections.emptyList()); - } - - @Test - public void makeHttpRequestsShouldBuildUrlIfAppIsPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))) - .build())) - .test(1) - .app(App.builder().name("name").bundle("bundle").domain("domain").build()) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).hasSize(1) - .extracting(HttpRequest::getUri) - .containsExactlyInAnyOrder("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0" - + "&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7&nc=1&nosecure=1" - + "&aid=adoceanmyaozpniqismex%3Aao-test&gdpr_consent=consent" - + "&gdpr=1&app=1&appname=name&appbundle=bundle&appdomain=domain"); - } - - @Test - public void makeHttpRequestsShouldBuildUrlIfDeviceWithIfaIsPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))) - .build())) - .test(1) - .device(Device.builder().ifa("ifa").os("os").osv("osv").model("model").make("make").build()) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).hasSize(1) - .extracting(HttpRequest::getUri) - .containsExactlyInAnyOrder("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0" - + "&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7" - + "&nc=1&nosecure=1&aid=adoceanmyaozpniqismex%3Aao-test" - + "&gdpr_consent=consent&gdpr=1&ifa=ifa&devos=os&devosv=osv&devmodel=model&devmake=make"); - } - - @Test - public void makeHttpRequestsShouldBuildUrlIfDeviceWithIfaIsNotPresent() { - // given - final BidRequest bidRequest = BidRequest.builder() - .user(User.builder() - .ext(ExtUser.builder() - .consent("consent").build()) - .build()) - .imp(singletonList(Imp.builder() - .id("ao-test") - .ext(mapper.valueToTree(ExtPrebid.of(null, - ExtImpAdocean.of("myao.adocean.pl", "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "adoceanmyaozpniqismex")))) - .build())) - .test(1) - .device(Device.builder().dpidmd5("dpidmd5").os("os").osv("osv").model("model").make("make").build()) - .build(); - - // when - final Result>> result = target.makeHttpRequests(bidRequest); - - // then - assertThat(result.getValue()).hasSize(1) - .extracting(HttpRequest::getUri) - .containsExactlyInAnyOrder("https://myao.adocean.pl/_10000000/ad.json?pbsrv_v=1.3.0" - + "&id=tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7" - + "&nc=1&nosecure=1&aid=adoceanmyaozpniqismex%3Aao-test" - + "&gdpr_consent=consent&gdpr=1&dpidmd5=dpidmd5&devos=os&devosv=osv" - + "&devmodel=model&devmake=make"); - } - - private static AdoceanResponseAdUnit adoceanResponseCreator( - Function adoceanCustomizer) { - return adoceanCustomizer.apply(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("false")) - .build(); - } - - 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 BidderCall givenHttpCall(String requestBody, String responseBody) - throws JsonProcessingException { - return BidderCall.succeededHttp( - HttpRequest.builder() - .body(mapper.writeValueAsBytes(requestBody)) - .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/java/org/prebid/server/bidder/clydo/ClydoBidderTest.java b/src/test/java/org/prebid/server/bidder/clydo/ClydoBidderTest.java new file mode 100644 index 00000000000..d4fc86456f5 --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/clydo/ClydoBidderTest.java @@ -0,0 +1,383 @@ +package org.prebid.server.bidder.clydo; + +import com.fasterxml.jackson.core.JsonProcessingException; +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.request.Video; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +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.clydo.ExtImpClydo; + +import java.util.List; +import java.util.function.UnaryOperator; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.prebid.server.proto.openrtb.ext.response.BidType.audio; +import static org.prebid.server.proto.openrtb.ext.response.BidType.banner; +import static org.prebid.server.proto.openrtb.ext.response.BidType.video; +import static org.prebid.server.proto.openrtb.ext.response.BidType.xNative; +import static org.prebid.server.util.HttpUtil.ACCEPT_HEADER; +import static org.prebid.server.util.HttpUtil.APPLICATION_JSON_CONTENT_TYPE; +import static org.prebid.server.util.HttpUtil.CONTENT_TYPE_HEADER; +import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE; + +public class ClydoBidderTest extends VertxTest { + + private static final String ENDPOINT_URL = "http://region={{Region}}.clydo.io/partnerId={{PartnerId}}"; + + private final ClydoBidder target = new ClydoBidder(ENDPOINT_URL, jacksonMapper); + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException().isThrownBy(() -> new ClydoBidder("invalid_url", jacksonMapper)); + } + + @Test + public void makeHttpRequestsShouldMakeOneRequestPerImp() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(asList( + givenImp(UnaryOperator.identity()), + givenImp(imp -> imp.id("321").ext(mapper.valueToTree(ExtPrebid + .of(null, ExtImpClydo.of("parentId", "us"))))))) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(2) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getImp) + .extracting(List::size) + .containsOnly(1); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getId) + .containsExactlyInAnyOrder("123", "321"); + } + + @Test + public void shouldMakeOneRequestWhenOneImpIsValidAndAnotherIsNot() { + // given + final BidRequest bidRequest = BidRequest.builder() + .imp(asList(givenImp(UnaryOperator.identity()), givenBadImp(UnaryOperator.identity()))) + .build(); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getId) + .containsExactly("123"); + } + + @Test + public void makeHttpRequestsShouldIncludeImpIds() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> impBuilder.id("imp1")); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getId) + .containsExactly("imp1"); + } + + @Test + public void makeHttpRequestsShouldUseCorrectUri() { + // given + final BidRequest bidRequest = givenBidRequest(UnaryOperator.identity()); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getUri) + .containsExactly("http://region=us.clydo.io/partnerId=parentId"); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfImpExtCannotBeParsed() { + // given + final BidRequest bidRequest = givenBidRequest(impBuilder -> + impBuilder.ext(mapper.createObjectNode().set("bidder", mapper.createArrayNode()))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).hasSize(1); + assertThat(result.getErrors().getFirst().getMessage()).startsWith("found no valid impressions"); + } + + @Test + public void makeHttpRequestsShouldReturnExpectedHeaders() { + // given + final BidRequest bidRequest = givenBidRequest(UnaryOperator.identity()); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getValue()).hasSize(1).first() + .extracting(HttpRequest::getHeaders) + .satisfies(headers -> assertThat(headers.get(CONTENT_TYPE_HEADER)) + .isEqualTo(APPLICATION_JSON_CONTENT_TYPE)) + .satisfies(headers -> assertThat(headers.get(ACCEPT_HEADER)) + .isEqualTo(APPLICATION_JSON_VALUE)); + assertThat(result.getErrors()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldUseRegionUsInEndpoint() { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .id("imp-us") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpClydo.of("partner123", "us"))))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).first() + .extracting(HttpRequest::getUri) + .isEqualTo("http://region=us.clydo.io/partnerId=partner123"); + } + + @Test + public void makeHttpRequestsShouldUseRegionEuInEndpointWithEuRegion() { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .id("imp-us") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpClydo.of("partner123", "eu"))))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).first() + .extracting(HttpRequest::getUri) + .isEqualTo("http://region=eu.clydo.io/partnerId=partner123"); + } + + @Test + public void makeHttpRequestsShouldUseDefaultRegionUsWhenRegionIsNull() { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .id("imp-null-region") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpClydo.of("partner123", null))))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1).first() + .extracting(HttpRequest::getUri) + .isEqualTo("http://region=us.clydo.io/partnerId=partner123"); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + mapper.writeValueAsString(null)); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnErrorIfResponseBodyCouldNotBeParsed() { + // given + final BidderCall httpCall = givenHttpCall("invalid"); + + // when + final Result> result = target.makeBids(httpCall, null); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getMessage()).startsWith("Failed to decode: Unrecognized token 'invalid':"); + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response); + }); + } + + @Test + public void makeBidsShouldReturnEmptyListIfBidResponseSeatBidIsNull() throws JsonProcessingException { + // given + final BidderCall httpCall = givenHttpCall( + 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 makeBidsShouldReturnBannerBid() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .banner(Banner.builder().w(300).h(250).build())); + final BidderCall httpCall = givenHttpCall( + givenBidResponse(bidBuilder -> bidBuilder.impid("123"))); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).extracting(BidderBid::getType).containsExactly(banner); + } + + @Test + public void makeBidsShouldReturnVideoBid() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .video(Video.builder().mimes(singletonList("video/mp4")).build())); + final BidderCall httpCall = givenHttpCall(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).extracting(BidderBid::getType).containsExactly(video); + } + + @Test + public void makeBidsShouldReturnNativeBid() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .xNative(Native.builder().request("{\"assets\":[]}").build())); + final BidderCall httpCall = givenHttpCall(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).extracting(BidderBid::getType).containsExactly(xNative); + } + + @Test + public void makeBidsShouldReturnAudioBid() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp + .banner(null) + .audio(Audio.builder().mimes(singletonList("audio/mp3")).build())); + final BidderCall httpCall = givenHttpCall(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).extracting(BidderBid::getType).containsExactly(audio); + } + + @Test + public void makeBidsShouldReturnErrorWhenImpHasNoMediaType() throws JsonProcessingException { + // given + final BidRequest bidRequest = givenBidRequest(imp -> imp.banner(null).video(null).xNative(null).audio(null)); + final BidderCall httpCall = givenHttpCall(givenBidResponse(bidBuilder -> bidBuilder.impid("123"))); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + assertThat(result.getValue()).isEmpty(); + assertThat(result.getErrors()).hasSize(1).first() + .satisfies(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_server_response); + assertThat(error.getMessage()).isEqualTo("Failed to get media type"); + }); + } + + private static BidRequest givenBidRequest( + UnaryOperator bidRequestCustomizer, + UnaryOperator impCustomizer) { + + return bidRequestCustomizer.apply(BidRequest.builder() + .imp(singletonList(givenImp(impCustomizer)))) + .build(); + } + + private static BidRequest givenBidRequest(UnaryOperator impCustomizer) { + return givenBidRequest(UnaryOperator.identity(), impCustomizer); + } + + private static Imp givenImp(UnaryOperator impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("123") + .ext(mapper.valueToTree(ExtPrebid.of(null, + ExtImpClydo.of("parentId", "us"))))) + .build(); + } + + private static Imp givenBadImp(UnaryOperator impCustomizer) { + return impCustomizer.apply(Imp.builder() + .id("invalidImp") + .ext(mapper.createObjectNode().set("bidder", mapper.createArrayNode()))) + .build(); + } + + private static String givenBidResponse(UnaryOperator bidCustomizer) throws JsonProcessingException { + final BidResponse bidResponse = BidResponse.builder() + .cur("USD") + .seatbid(singletonList(SeatBid.builder().bid(singletonList(bidCustomizer.apply(Bid.builder()).build())) + .build())) + .build(); + return mapper.writeValueAsString(bidResponse); + } + + private static BidderCall givenHttpCall(String body) { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(null).build(), + HttpResponse.of(200, null, body), + null); + } +} diff --git a/src/test/java/org/prebid/server/bidder/nexx360/Nexx360BidderTest.java b/src/test/java/org/prebid/server/bidder/nexx360/Nexx360BidderTest.java index 09c50589e19..bb06e21635b 100644 --- a/src/test/java/org/prebid/server/bidder/nexx360/Nexx360BidderTest.java +++ b/src/test/java/org/prebid/server/bidder/nexx360/Nexx360BidderTest.java @@ -28,6 +28,7 @@ import java.math.BigDecimal; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.function.UnaryOperator; import static java.util.Collections.singletonList; @@ -133,6 +134,30 @@ public void makeHttpRequestsShouldModifyImpExt() { .containsExactly(expectedExt1, expectedExt2); } + @Test + public void makeHttpRequestsShouldPreserveCustomFieldsInImpExt() { + // given + final BidRequest bidRequest = givenBidRequest( + imp -> imp.id("imp1").ext(mapper.valueToTree(Map.of( + "bidder", ExtImpNexx360.of("tag1", "p1"), + "customField", "customValue")))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + final ObjectNode expectedExt = mapper.valueToTree(Map.of( + "nexx360", ExtImpNexx360.of("tag1", "p1"), + "customField", "customValue")); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getExt) + .containsExactly(expectedExt); + } + @Test public void makeHttpRequestsShouldModifyRequestExt() { // given diff --git a/src/test/java/org/prebid/server/bidder/smarthub/SmarthubBidderTest.java b/src/test/java/org/prebid/server/bidder/smarthub/SmarthubBidderTest.java index 3f5630718e2..c9defbbd2e8 100644 --- a/src/test/java/org/prebid/server/bidder/smarthub/SmarthubBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/smarthub/SmarthubBidderTest.java @@ -17,7 +17,6 @@ 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.adocean.ExtImpAdocean; import org.prebid.server.proto.openrtb.ext.request.smarthub.ExtImpSmarthub; import org.prebid.server.proto.openrtb.ext.response.BidType; import org.prebid.server.proto.openrtb.ext.response.ExtBidResponse; @@ -178,7 +177,7 @@ public void makeBidsShouldReturnErrorIfExtIncorrect() throws JsonProcessingExcep // given final BidderCall httpCall = givenHttpCall(givenBidRequest(identity()), mapper.writeValueAsString(givenBidResponse(builder -> builder.ext(mapper.valueToTree( - ExtPrebid.of(null, ExtImpAdocean.of("someEmitterDomain", "someMasterId", "someSlaveID"))))))); + ExtPrebid.of(null, ExtImpSmarthub.of("someEmitterDomain", "someMasterId", "someSlaveID"))))))); // when final Result> result = target.makeBids(httpCall, null); diff --git a/src/test/java/org/prebid/server/bidder/teal/TealBidderTest.java b/src/test/java/org/prebid/server/bidder/teal/TealBidderTest.java new file mode 100644 index 00000000000..200902919df --- /dev/null +++ b/src/test/java/org/prebid/server/bidder/teal/TealBidderTest.java @@ -0,0 +1,273 @@ +package org.prebid.server.bidder.teal; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +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.request.Publisher; +import com.iab.openrtb.request.Site; +import com.iab.openrtb.request.Video; +import com.iab.openrtb.response.Bid; +import com.iab.openrtb.response.BidResponse; +import com.iab.openrtb.response.SeatBid; +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.teal.ExtImpTeal; +import org.prebid.server.proto.openrtb.ext.response.BidType; + +import java.math.BigDecimal; +import java.util.List; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +public class TealBidderTest extends VertxTest { + + private static final String ENDPOINT_URL = "https://test.endpoint.com/"; + + private final TealBidder target = new TealBidder(ENDPOINT_URL, jacksonMapper); + + @Test + public void creationShouldFailOnInvalidEndpointUrl() { + assertThatIllegalArgumentException().isThrownBy(() -> new TealBidder("invalid_url", jacksonMapper)); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { + // given + final BidRequest bidRequest = givenBidRequest( + imp -> imp.ext(mapper.valueToTree(ExtPrebid.of(null, mapper.createArrayNode())))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()).startsWith("Error parsing imp.ext for impression impId"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfAccountParamFailsValidation() { + // given + final BidRequest bidRequest = givenBidRequest( + imp -> imp.id("imp1").ext(givenImpExt("", "placement"))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()).startsWith("account parameter failed validation"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldReturnErrorIfPlacementParamFailsValidation() { + // given + final BidRequest bidRequest = givenBidRequest( + imp -> imp.id("imp1").ext(givenImpExt("account", ""))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).hasSize(1) + .allSatisfy(error -> { + assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input); + assertThat(error.getMessage()).startsWith("placement parameter failed validation"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeHttpRequestsShouldMapParametersCorrectly() { + // given + final BidRequest bidRequest = givenBidRequest( + Site.builder().publisher(Publisher.builder().domain("mydomain.com").build()).build(), + imp -> imp.id("imp1").ext(givenImpExt("account", "placement1")), + imp -> imp.id("imp2").ext(givenImpExt("account", null))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + //then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getSite) + .extracting(Site::getPublisher) + .extracting(Publisher::getId) + .containsExactly("account"); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getExt) + .element(0) + .extracting(ext -> ext.get("prebid")) + .extracting(prebid -> prebid.get("storedrequest")) + .extracting(storedRequest -> storedRequest.get("id")) + .extracting(JsonNode::textValue) + .isEqualTo("placement1"); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .flatExtracting(BidRequest::getImp) + .extracting(Imp::getExt) + .element(1) + .extracting(ext -> ext.get("prebid")) + .isNull(); + } + + @Test + public void makeHttpRequestsShouldAddExtBidsPBSFlag() { + // given + final BidRequest bidRequest = givenBidRequest( + imp -> imp.id("imp1").ext(givenImpExt("account", "placement1"))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + //then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()) + .extracting(HttpRequest::getPayload) + .extracting(BidRequest::getExt) + .extracting(ext -> ext.getProperty("bids")) + .extracting(bids -> bids.get("pbs").toString()) + .containsExactly("1"); + } + + @Test + public void makeBidsShouldReturnErrorWhenResponseBodyCouldNotBeParsed() { + // given + final BidderCall httpCall = BidderCall.succeededHttp( + HttpRequest.builder().build(), + HttpResponse.of(200, null, "invalid_json"), + null); + + // 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 'invalid_json'"); + }); + assertThat(result.getValue()).isEmpty(); + } + + @Test + public void makeBidsShouldReturnBannerBid() throws JsonProcessingException { + // given + final Bid responseBid = givenBid("imp1", 1, 1); + final BidRequest bidRequest = givenBidRequest(imp -> imp.id("imp1").banner(Banner.builder().id("id").build())); + final BidderCall httpCall = givenHttpCall(bidRequest, singletonList(responseBid)); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + final Bid expectedBid = givenBid("imp1", 1, 1); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).containsExactly(BidderBid.of(expectedBid, BidType.banner, "USD")); + } + + @Test + public void makeBidsShouldReturnVideoBid() throws JsonProcessingException { + // given + final Bid responseBid = givenBid("imp1", 2, 1); + final BidRequest bidRequest = givenBidRequest(imp -> imp.id("imp1").video(Video.builder().pos(1).build())); + final BidderCall httpCall = givenHttpCall(bidRequest, singletonList(responseBid)); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + final Bid expectedBid = givenBid("imp1", 2, 1); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).containsExactly(BidderBid.of(expectedBid, BidType.video, "USD")); + } + + @Test + public void makeBidsShouldReturnNativeBid() throws JsonProcessingException { + // given + final Bid responseBid = givenBid("imp1", 4, 1); + final BidRequest bidRequest = givenBidRequest(imp -> imp.id("imp1").xNative(Native.builder().ver("1").build())); + final BidderCall httpCall = givenHttpCall(bidRequest, singletonList(responseBid)); + + // when + final Result> result = target.makeBids(httpCall, bidRequest); + + // then + final Bid expectedBid = givenBid("imp1", 4, 1); + + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).containsExactly(BidderBid.of(expectedBid, BidType.xNative, "USD")); + } + + private static BidRequest givenBidRequest(UnaryOperator impCustomizer) { + return givenBidRequest(null, impCustomizer); + } + + private static BidRequest givenBidRequest(Site site, UnaryOperator... impCustomizers) { + final List imps = Stream.of(impCustomizers) + .map(customizer -> customizer.apply(Imp.builder().id("impId")).build()) + .toList(); + return BidRequest.builder().imp(imps).site(site).build(); + } + + private static ObjectNode givenImpExt(String account, String placement) { + return mapper.valueToTree(ExtPrebid.of(null, ExtImpTeal.of(account, placement))); + } + + private static Bid givenBid(String id, Integer mType, int price) { + return Bid.builder() + .id("teal-" + id) + .impid(id) + .mtype(mType) + .price(BigDecimal.valueOf(price)) + .adm("adm") + .w(300) + .h(250) + .adomain(singletonList("adomain.com")) + .ext(mapper.createObjectNode()) + .build(); + } + + private static BidResponse givenBidResponse(List bids) { + return BidResponse.builder() + .cur("USD") + .seatbid(singletonList(SeatBid.builder().bid(bids).build())) + .build(); + } + + private static BidderCall givenHttpCall(BidRequest bidRequest, List bids) + throws JsonProcessingException { + return BidderCall.succeededHttp( + HttpRequest.builder().payload(bidRequest).build(), + HttpResponse.of(200, null, mapper.writeValueAsString(givenBidResponse(bids))), + null); + } +} diff --git a/src/test/java/org/prebid/server/it/AdoceanTest.java b/src/test/java/org/prebid/server/it/AdoceanTest.java deleted file mode 100644 index d1b0da1f1fc..00000000000 --- a/src/test/java/org/prebid/server/it/AdoceanTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.prebid.server.it; - -import com.github.tomakehurst.wiremock.client.WireMock; -import io.restassured.response.Response; -import org.json.JSONException; -import org.junit.jupiter.api.Test; -import org.prebid.server.model.Endpoint; - -import java.io.IOException; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static java.util.Collections.singletonList; - -public class AdoceanTest extends IntegrationTest { - - @Test - public void openrtb2AuctionShouldRespondWithBidsFromAdocean() throws IOException, JSONException { - - WIRE_MOCK_RULE.stubFor(get(WireMock.urlPathMatching("/adocean-exchange/_[0-9]*/ad.json")) - .willReturn(aResponse().withBody(jsonFrom("openrtb2/adocean/test-adocean-bid-response-1.json")))); - - // when - final Response response = responseFor("openrtb2/adocean/test-auction-adocean-request.json", - Endpoint.openrtb2_auction); - - // then - assertJsonEquals("openrtb2/adocean/test-auction-adocean-response.json", response, singletonList("adocean")); - } -} diff --git a/src/test/java/org/prebid/server/it/MobuppsTest.java b/src/test/java/org/prebid/server/it/ClydoTest.java similarity index 58% rename from src/test/java/org/prebid/server/it/MobuppsTest.java rename to src/test/java/org/prebid/server/it/ClydoTest.java index 9987de46469..6bb262350a3 100644 --- a/src/test/java/org/prebid/server/it/MobuppsTest.java +++ b/src/test/java/org/prebid/server/it/ClydoTest.java @@ -13,21 +13,20 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singletonList; -public class MobuppsTest extends IntegrationTest { +public class ClydoTest extends IntegrationTest { @Test - public void openrtb2AuctionShouldRespondWithBidsFromTheMobupps() throws IOException, JSONException { + public void openrtb2AuctionShouldRespondWithBidsFromClydo() throws IOException, JSONException { // given - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/mobupps-exchange")) - .withRequestBody(equalToJson(jsonFrom("openrtb2/mobupps/test-mobupps-bid-request.json"), true, true)) - .willReturn(aResponse().withBody(jsonFrom("openrtb2/mobupps/test-mobupps-bid-response.json")))); + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/clydo-exchange")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/clydo/test-clydo-bid-request.json"))) + .willReturn(aResponse().withBody(jsonFrom("openrtb2/clydo/test-clydo-bid-response.json")))); // when - final Response response = responseFor("openrtb2/mobupps/test-auction-mobupps-request.json", + final Response response = responseFor("openrtb2/clydo/test-auction-clydo-request.json", Endpoint.openrtb2_auction); // then - assertJsonEquals("openrtb2/mobupps/test-auction-mobupps-response.json", response, - singletonList("mobupps")); + assertJsonEquals("openrtb2/clydo/test-auction-clydo-response.json", response, singletonList("clydo")); } } diff --git a/src/test/java/org/prebid/server/it/ProgxTest.java b/src/test/java/org/prebid/server/it/ProgxTest.java index 6f77d217a86..c84a146ba7d 100644 --- a/src/test/java/org/prebid/server/it/ProgxTest.java +++ b/src/test/java/org/prebid/server/it/ProgxTest.java @@ -18,7 +18,7 @@ public class ProgxTest extends IntegrationTest { @Test public void openrtb2AuctionShouldRespondWithBidsFromProgx() throws IOException, JSONException { // given - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/progx-exchange/connectionId")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/progx-exchange/")) .withRequestBody(equalToJson(jsonFrom("openrtb2/progx/test-progx-bid-request.json"))) .willReturn(aResponse().withBody(jsonFrom("openrtb2/progx/test-progx-bid-response.json")))); diff --git a/src/test/java/org/prebid/server/it/VimayxTest.java b/src/test/java/org/prebid/server/it/RadianfusionTest.java similarity index 59% rename from src/test/java/org/prebid/server/it/VimayxTest.java rename to src/test/java/org/prebid/server/it/RadianfusionTest.java index ac7750065d0..52b401de7b3 100644 --- a/src/test/java/org/prebid/server/it/VimayxTest.java +++ b/src/test/java/org/prebid/server/it/RadianfusionTest.java @@ -14,23 +14,25 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static java.util.Collections.singletonList; -public class VimayxTest extends IntegrationTest { +public class RadianfusionTest extends IntegrationTest { @Test - public void openrtb2AuctionShouldRespondWithBidsFromVimayx() throws IOException, JSONException { + public void openrtb2AuctionShouldRespondWithBidsFromRadianfusion() throws IOException, JSONException { // given - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/vimayx-exchange")) + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/radianfusion-exchange")) .withQueryParam("host", equalTo("someUniquePartnerName")) .withQueryParam("accountId", equalTo("someSeat")) .withQueryParam("sourceId", equalTo("someToken")) - .withRequestBody(equalToJson(jsonFrom("openrtb2/vimayx/test-vimayx-bid-request.json"))) - .willReturn(aResponse().withBody(jsonFrom("openrtb2/vimayx/test-vimayx-bid-response.json")))); + .withRequestBody(equalToJson(jsonFrom("openrtb2/radianfusion/test-radianfusion-bid-request.json"))) + .willReturn(aResponse().withBody( + jsonFrom("openrtb2/radianfusion/test-radianfusion-bid-response.json")))); // when - final Response response = responseFor("openrtb2/vimayx/test-auction-vimayx-request.json", + final Response response = responseFor("openrtb2/radianfusion/test-auction-radianfusion-request.json", Endpoint.openrtb2_auction); // then - assertJsonEquals("openrtb2/vimayx/test-auction-vimayx-response.json", response, singletonList("vimayx")); + assertJsonEquals("openrtb2/radianfusion/test-auction-radianfusion-response.json", + response, singletonList("radianfusion")); } } diff --git a/src/test/java/org/prebid/server/it/TealTest.java b/src/test/java/org/prebid/server/it/TealTest.java new file mode 100644 index 00000000000..bad01a8fb69 --- /dev/null +++ b/src/test/java/org/prebid/server/it/TealTest.java @@ -0,0 +1,32 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.prebid.server.model.Endpoint; + +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; + +public class TealTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromTeal() throws IOException, JSONException { + // given + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/teal-exchange")) + .withRequestBody(equalToJson(jsonFrom("openrtb2/teal/test-teal-bid-request.json"))) + .willReturn(aResponse().withBody(jsonFrom("openrtb2/teal/test-teal-bid-response.json")))); + + // when + final Response response = responseFor("openrtb2/teal/test-auction-teal-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/teal/test-auction-teal-response.json", response, singletonList("teal")); + } +} diff --git a/src/test/java/org/prebid/server/it/ThreeSixtyPlayVidTest.java b/src/test/java/org/prebid/server/it/ThreeSixtyPlayVidTest.java new file mode 100644 index 00000000000..b87be532e46 --- /dev/null +++ b/src/test/java/org/prebid/server/it/ThreeSixtyPlayVidTest.java @@ -0,0 +1,37 @@ +package org.prebid.server.it; + +import io.restassured.response.Response; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.prebid.server.model.Endpoint; + +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; + +public class ThreeSixtyPlayVidTest extends IntegrationTest { + + private static final String BID_REQUEST_JSON = "openrtb2/360playvid/test-360playvid-bid-request.json"; + private static final String BID_RESPONSE_JSON = "openrtb2/360playvid/test-360playvid-bid-response.json"; + private static final String AUCTION_REQUEST_JSON = "openrtb2/360playvid/test-auction-360playvid-request.json"; + private static final String AUCTION_RESPONSE_JSON = "openrtb2/360playvid/test-auction-360playvid-response.json"; + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromThreeSixtyPlayVid() throws IOException, JSONException { + // given + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/360playvid-exchange")) + .withRequestBody(equalToJson(jsonFrom(BID_REQUEST_JSON))) + .willReturn(aResponse().withBody(jsonFrom(BID_RESPONSE_JSON)))); + + // when + final Response response = responseFor(AUCTION_REQUEST_JSON, + Endpoint.openrtb2_auction); + + // then + assertJsonEquals(AUCTION_RESPONSE_JSON, response, singletonList("360playvid")); + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-360playvid-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-360playvid-bid-request.json new file mode 100644 index 00000000000..dcf904ba566 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-360playvid-bid-request.json @@ -0,0 +1,56 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "secure": 1, + "ext": { + "bidder": { + "type": "publisher", + "placementId": "testPlacementId" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "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/360playvid/test-360playvid-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-360playvid-bid-response.json new file mode 100644 index 00000000000..180173549d8 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-360playvid-bid-response.json @@ -0,0 +1,21 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 3.33, + "crid": "creativeId", + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-request.json new file mode 100644 index 00000000000..7f9d01b6a92 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-request.json @@ -0,0 +1,23 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "360playvid": { + "placementId": "testPlacementId" + } + } + } + ], + "tmax": 5000, + "regs": { + "ext": { + "gdpr": 0 + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-response.json new file mode 100644 index 00000000000..9072fe49720 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/360playvid/test-auction-360playvid-response.json @@ -0,0 +1,38 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "exp": 300, + "price": 3.33, + "crid": "creativeId", + "mtype": 1, + "ext": { + "origbidcpm": 3.33, + "prebid": { + "type": "banner", + "meta": { + "adaptercode": "360playvid" + } + } + } + } + ], + "seat": "360playvid", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "360playvid": "{{ 360playvid.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} \ No newline at end of file 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 deleted file mode 100644 index 24304dd8da4..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "id": "adoceanmyaozpniqismex", - "price": "10", - "winurl": "https://win-url.com", - "statsUrl": "https://stats-url.com", - "code": " ", - "currency": "USD", - "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 deleted file mode 100644 index eccdf164b32..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "id": "request_id", - "imp": [ - { - "id": "imp_id", - "banner": { - "w": 300, - "h": 250 - }, - "ext": { - "prebid": { - "bidder": { - "adocean": { - "emitterPrefix": "myao", - "masterId": "tmYF.DMl7ZBq.Nqt2Bq4FutQTJfTpxCOmtNPZoQUDcL.G7", - "slaveId": "adoceanmyaozpniqismex" - } - } - } - } - } - ], - "tmax": 5000, - "regs": { - "ext": { - "gdpr": 0 - } - } -} 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 deleted file mode 100644 index ea7c23c5bb4..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "id": "request_id", - "seatbid": [ - { - "bid": [ - { - "id": "adoceanmyaozpniqismex", - "impid": "imp_id", - "exp": 300, - "price": 10, - "adm": " ", - "crid": "0af345b42983cc4bc0", - "w": 300, - "h": 250, - "ext": { - "prebid": { - "type": "banner", - "meta": { - "adaptercode": "adocean" - } - }, - "origbidcpm": 10, - "origbidcur": "USD" - } - } - ], - "seat": "adocean", - "group": 0 - } - ], - "cur": "USD", - "ext": { - "responsetimemillis": { - "adocean": "{{ adocean.response_time_ms }}" - }, - "tmaxrequest": 5000, - "prebid": { - "auctiontimestamp": 0 - } - } -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-request.json b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-request.json new file mode 100644 index 00000000000..bfe3de91342 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-request.json @@ -0,0 +1,24 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "clydo": { + "region": "us", + "partnerId": "testPartnerId" + } + } + } + ], + "tmax": 5000, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-response.json b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-response.json new file mode 100644 index 00000000000..4b1494175f4 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-response.json @@ -0,0 +1,42 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "exp": 300, + "price": 3.33, + "adm": "adm001", + "adid": "adid", + "cid": "cid", + "crid": "crid", + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner", + "meta": { + "adaptercode": "clydo" + } + }, + "origbidcpm": 3.33 + } + } + ], + "seat": "clydo", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "clydo": "{{ clydo.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json similarity index 50% rename from src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json rename to src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json index b1a0a5c0c8b..e43823a0317 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json @@ -1,38 +1,37 @@ { - "id": "test-auction-request", + "id": "request_id", "imp": [ { - "id": "imp1", + "id": "imp_id", + "secure": 1, "banner": { - "format": [ - { - "w": 300, - "h": 250 - } - ] + "w": 300, + "h": 250 }, - "secure": 1, "ext": { "tid": "${json-unit.any-string}", "bidder": { - "adUnitId": 1, - "auth": "123456" + "region": "us", + "partnerId": "testPartnerId" } } } ], + "source": { + "tid": "${json-unit.any-string}" + }, "site": { - "domain": "testpage.com", - "page": "http://testpage.com", + "domain": "www.example.com", + "page": "http://www.example.com", "publisher": { - "domain": "testpage.com" + "domain": "example.com" }, "ext": { "amp": 0 } }, "device": { - "ua": "Mozilla/5.0", + "ua": "userAgent", "ip": "193.168.244.1" }, "at": 1, @@ -40,12 +39,19 @@ "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/clydo/test-clydo-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-response.json new file mode 100644 index 00000000000..46fdbbec2ad --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-response.json @@ -0,0 +1,20 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 3.33, + "adid": "adid", + "crid": "crid", + "cid": "cid", + "adm": "adm001", + "h": 250, + "w": 300 + } + ] + } + ] +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/easybid/test-easybid-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/easybid/test-easybid-bid-request.json index a6a87d4012c..94c35c9da4d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/easybid/test-easybid-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/easybid/test-easybid-bid-request.json @@ -9,6 +9,7 @@ }, "secure": 1, "ext": { + "tid": "${json-unit.any-string}", "nexx360": { "placement": "placement" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-request.json deleted file mode 100644 index 13f73b2a640..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-request.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id": "test-auction-request", - "imp": [ - { - "id": "imp1", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - } - ] - }, - "ext": { - "mobupps": { - "adUnitId": 1, - "auth": "123456" - } - } - } - ], - "site": { - "page": "http://testpage.com" - }, - "device": { - "ua": "Mozilla/5.0" - }, - "tmax": 5000, - "regs": { - "ext": { - "gdpr": 0 - } - } -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-response.json b/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-response.json deleted file mode 100644 index 6019f0e1752..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-response.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "id": "test-auction-request", - "seatbid": [ - { - "seat": "mobupps", - "group": 0, - "bid": [ - { - "id": "bid1", - "impid": "imp1", - "price": 1.23, - "adm": "", - "nurl": "https://example.com/win?price=1.23", - "crid": "creative1", - "w": 300, - "h": 250, - "exp": 300, - "mtype": 1, - "ext": { - "origbidcpm": 1.23, - "origbidcur": "USD", - "prebid": { - "type": "banner", - "meta": { - "adaptercode": "mobupps" - } - } - } - } - ] - } - ], - "cur": "USD", - "ext": { - "responsetimemillis": { - "mobupps": 0 - }, - "tmaxrequest": 5000, - "prebid": { - "auctiontimestamp": 0 - } - } -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-response.json deleted file mode 100644 index 735fcd2ee33..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-response.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "test-auction-request", - "seatbid": [ - { - "seat": "mobupps", - "group": 0, - "bid": [ - { - "id": "bid1", - "impid": "imp1", - "price": 1.23, - "adm": "", - "nurl": "https://example.com/win?price=1.23", - "crid": "creative1", - "w": 300, - "h": 250, - "exp": 300, - "mtype": 1, - "ext": { - "origbidcpm": 1.23, - "origbidcur": "USD", - "prebid": { - "type": "banner" - } - } - } - ] - } - ], - "cur": "USD", - "ext": { - "responsetimemillis": { - "mobupps": 0 - }, - "tmaxrequest": 5000, - "prebid": { - "auctiontimestamp": 0 - } - } -} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/nexx360/test-nexx360-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/nexx360/test-nexx360-bid-request.json index a6a87d4012c..94c35c9da4d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/nexx360/test-nexx360-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/nexx360/test-nexx360-bid-request.json @@ -9,6 +9,7 @@ }, "secure": 1, "ext": { + "tid": "${json-unit.any-string}", "nexx360": { "placement": "placement" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/oneaccord/test-1accord-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/oneaccord/test-1accord-bid-request.json index a6a87d4012c..94c35c9da4d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/oneaccord/test-1accord-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/oneaccord/test-1accord-bid-request.json @@ -9,6 +9,7 @@ }, "secure": 1, "ext": { + "tid": "${json-unit.any-string}", "nexx360": { "placement": "placement" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/prismassp/test-prismassp-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/prismassp/test-prismassp-bid-request.json index a6a87d4012c..94c35c9da4d 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/prismassp/test-prismassp-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/prismassp/test-prismassp-bid-request.json @@ -9,6 +9,7 @@ }, "secure": 1, "ext": { + "tid": "${json-unit.any-string}", "nexx360": { "placement": "placement" } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-request.json index 3c416a84304..c1994dd61a0 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-request.json @@ -3,14 +3,13 @@ "imp": [ { "id": "imp_id", - "secure": 1, "banner": { - "w": 320, + "w": 300, "h": 250 }, "ext": { "progx": { - "cId": "connectionId" + "placementId": "testPlacementId" } } } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-response.json b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-response.json index b7437013277..59f6dffcf46 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-auction-progx-response.json @@ -7,19 +7,17 @@ "id": "bid_id", "impid": "imp_id", "exp": 300, - "price": 0.01, - "adid": "2068416", - "cid": "8048", - "crid": "24080", + "price": 3.33, + "crid": "creativeId", "mtype": 1, "ext": { + "origbidcpm": 3.33, "prebid": { "type": "banner", "meta": { "adaptercode": "progx" } - }, - "origbidcpm": 0.01 + } } } ], diff --git a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-request.json index eb67f5687e5..dcf904ba566 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-request.json @@ -3,22 +3,19 @@ "imp": [ { "id": "imp_id", - "secure": 1, "banner": { - "w": 320, + "w": 300, "h": 250 }, + "secure": 1, "ext": { - "tid": "${json-unit.any-string}", "bidder": { - "cId": "connectionId" + "type": "publisher", + "placementId": "testPlacementId" } } } ], - "source": { - "tid": "${json-unit.any-string}" - }, "site": { "domain": "www.example.com", "page": "http://www.example.com", @@ -38,8 +35,13 @@ "cur": [ "USD" ], + "source": { + "tid": "${json-unit.any-string}" + }, "regs": { + "ext": { "gdpr": 0 + } }, "ext": { "prebid": { diff --git a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-response.json index 47d4f8718ea..180173549d8 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/progx/test-progx-bid-response.json @@ -1,19 +1,21 @@ { - "id": "tid", + "id": "request_id", "seatbid": [ { "bid": [ { - "crid": "24080", - "adid": "2068416", - "price": 0.01, "id": "bid_id", "impid": "imp_id", - "cid": "8048", - "mtype": 1 + "price": 3.33, + "crid": "creativeId", + "mtype": 1, + "ext": { + "prebid": { + "type": "banner" + } + } } - ], - "type": "banner" + ] } ] } diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-request.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json similarity index 93% rename from src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-request.json rename to src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json index 37f89d39672..c4c23330095 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-request.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json @@ -8,7 +8,7 @@ "h": 250 }, "ext": { - "vimayx": { + "radianfusion": { "partnerName": "someUniquePartnerName", "seat": "someSeat", "token": "someToken" diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-response.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json similarity index 84% rename from src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-response.json rename to src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json index 218b16cda60..701a84c994b 100644 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-response.json +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json @@ -20,20 +20,20 @@ "prebid": { "type": "video", "meta": { - "adaptercode": "vimayx" + "adaptercode": "radianfusion" } } } } ], - "seat": "vimayx", + "seat": "radianfusion", "group": 0 } ], "cur": "USD", "ext": { "responsetimemillis": { - "vimayx": "{{ vimayx.response_time_ms }}" + "radianfusion": "{{ radianfusion.response_time_ms }}" }, "prebid": { "auctiontimestamp": 0 diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json similarity index 100% rename from src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-request.json rename to src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json diff --git a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-response.json similarity index 100% rename from src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-response.json rename to src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-response.json diff --git a/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-request.json b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-request.json new file mode 100644 index 00000000000..af84f5cf1b7 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-request.json @@ -0,0 +1,44 @@ +{ + "id": "test-request-banner", + "imp": [ + { + "id": "test-imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "teal": { + "account": "test-account", + "placement": "test-placement300x250" + } + } + } + ], + "site": { + "id": "demo-site", + "domain": "example.com", + "page": "https://example.com/demo", + "publisher": { + "id": "demo-publisher" + } + }, + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "ip": "192.0.2.1", + "language": "en", + "dnt": 0 + }, + "user": { + "id": "demo-user" + }, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-response.json b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-response.json new file mode 100644 index 00000000000..e183f1a0330 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-auction-teal-response.json @@ -0,0 +1,41 @@ +{ + "id": "test-request-banner", + "seatbid": [ + { + "seat": "teal", + "bid": [ + { + "id": "test-imp-banner", + "impid": "test-imp-banner", + "price": 2.50, + "adm": "
Teal Demo Ad
", + "w": 300, + "h": 250, + "crid": "demo-creative-123", + "exp": 300, + "ext": { + "origbidcpm": 2.50, + "origbidcur": "USD", + "prebid": { + "type": "banner", + "meta": { + "adaptercode": "teal" + } + } + } + } + ], + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "teal": "{{ teal.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-request.json new file mode 100644 index 00000000000..c26cb49987d --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-request.json @@ -0,0 +1,77 @@ +{ + "id": "test-request-banner", + "imp": [ + { + "id": "test-imp-banner", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "secure": 1, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "account": "test-account", + "placement": "test-placement300x250" + }, + "prebid": { + "storedrequest": { + "id": "test-placement300x250" + } + } + } + } + ], + "site": { + "id": "demo-site", + "domain": "example.com", + "page": "https://example.com/demo", + "publisher": { + "id": "test-account", + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "dnt": 0, + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "ip": "192.0.2.1", + "language": "en" + }, + "user": { + "id": "demo-user" + }, + "at": 1, + "tmax": "${json-unit.any-number}", + "cur": [ + "USD" + ], + "source": { + "tid": "${json-unit.any-string}" + }, + "regs": { + "gdpr": 0 + }, + "ext": { + "prebid": { + "channel": { + "name": "web" + }, + "server": { + "externalurl": "http://localhost:8080", + "gvlid": 1, + "datacenter": "local", + "endpoint": "/openrtb2/auction" + } + }, + "bids": { + "pbs": 1 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-response.json new file mode 100644 index 00000000000..2c5a13641ff --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/teal/test-teal-bid-response.json @@ -0,0 +1,29 @@ +{ + "id": "test-request-banner", + "seatbid": [ + { + "bid": [ + { + "id": "test-imp-banner", + "impid": "test-imp-banner", + "price": 2.50, + "adm": "
Teal Demo Ad
", + "w": 300, + "h": 250, + "crid": "demo-creative-123", + "exp": 300, + "ext": { + "origbidcpm": 2.50, + "origbidcur": "USD", + "prebid": { + "type": "banner", + "meta": { + "adaptercode": "teal" + } + } + } + } + ] + } + ] +} 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 2a3ffe37083..ae0a683c9a7 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -62,8 +62,6 @@ adapters.admixer.endpoint=http://localhost:8090/admixer-exchange adapters.adnuntius.enabled=true adapters.adnuntius.endpoint=http://localhost:8090/adnuntius-exchange adapters.adnuntius.eu-endpoint=http://localhost:8090/adnuntius-exchange-eu -adapters.adocean.enabled=true -adapters.adocean.endpoint=http://localhost:8090/adocean-exchange adapters.elementaltv.enabled=true adapters.elementaltv.endpoint=http://localhost:8090/elementaltv-exchange adapters.adpone.enabled=true @@ -76,8 +74,6 @@ adapters.adverxo.aliases.adport.enabled=true adapters.adverxo.aliases.adport.endpoint=http://localhost:8090/adport-exchange adapters.adverxo.aliases.bidsmind.enabled=true adapters.adverxo.aliases.bidsmind.endpoint=http://localhost:8090/bidsmind-exchange -adapters.adverxo.aliases.mobupps.enabled=true -adapters.adverxo.aliases.mobupps.endpoint=http://localhost:8090/mobupps-exchange adapters.adview.enabled=true adapters.adview.endpoint=http://localhost:8090/adview-exchange?accountId={{AccountId}} adapters.adprime.enabled=true @@ -191,6 +187,8 @@ adapters.brave.enabled=true adapters.brave.endpoint=http://localhost:8090/brave-exchange adapters.bwx.enabled=true adapters.bwx.endpoint=http://localhost:8090/bwx-exchange +adapters.clydo.enabled=true +adapters.clydo.endpoint=http://localhost:8090/clydo-exchange adapters.cointraffic.enabled=true adapters.cointraffic.endpoint=http://localhost:8090/cointraffic-exchange adapters.connatix.enabled=true @@ -521,8 +519,6 @@ adapters.smarthub.aliases.jdpmedia.enabled=true adapters.smarthub.aliases.jdpmedia.endpoint=http://localhost:8090/jdpmedia-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} adapters.smarthub.aliases.tredio.enabled=true adapters.smarthub.aliases.tredio.endpoint=http://localhost:8090/tredio-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} -adapters.smarthub.aliases.vimayx.enabled=true -adapters.smarthub.aliases.vimayx.endpoint=http://localhost:8090/vimayx-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} adapters.smarthub.aliases.felixads.enabled=true adapters.smarthub.aliases.felixads.endpoint=http://localhost:8090/felixads-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} adapters.smarthub.aliases.jambojar.enabled=true @@ -533,6 +529,8 @@ adapters.smarthub.aliases.addigi.enabled=true adapters.smarthub.aliases.addigi.endpoint=http://localhost:8090/addigi-exchange adapters.smarthub.aliases.artechnology.enabled=true adapters.smarthub.aliases.artechnology.endpoint=http://localhost:8090/artechnology-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} +adapters.smarthub.aliases.radianfusion.enabled=true +adapters.smarthub.aliases.radianfusion.endpoint=http://localhost:8090/radianfusion-exchange?host={{Host}}&accountId={{AccountID}}&sourceId={{SourceId}} adapters.smartyads.enabled=true adapters.smartyads.endpoint=http://localhost:8090/smartyads-exchange adapters.smilewanted.enabled=true @@ -573,10 +571,14 @@ adapters.tappx.enabled=true adapters.tappx.endpoint=http://localhost:8090/tappx-exchange adapters.teads.enabled=true adapters.teads.endpoint=http://localhost:8090/teads-exchange +adapters.teal.enabled=true +adapters.teal.endpoint=http://localhost:8090/teal-exchange adapters.telaria.enabled=true adapters.telaria.endpoint=http://localhost:8090/telaria-exchange/ adapters.teqblaze.enabled=true adapters.teqblaze.endpoint=http://localhost:8090/teqblaze-exchange +adapters.teqblaze.aliases.360playvid.enabled=true +adapters.teqblaze.aliases.360playvid.endpoint=http://localhost:8090/360playvid-exchange adapters.teqblaze.aliases.pinkLion.enabled=true adapters.teqblaze.aliases.pinkLion.endpoint=http://localhost:8090/pinkLion-exchange adapters.teqblaze.aliases.rocketlab.enabled=true @@ -585,6 +587,8 @@ adapters.teqblaze.aliases.appStockSSP.enabled=true adapters.teqblaze.aliases.appStockSSP.endpoint=http://localhost:8090/appstockssp-exchange adapters.teqblaze.aliases.gravite.enabled=true adapters.teqblaze.aliases.gravite.endpoint=http://localhost:8090/gravite-exchange +adapters.teqblaze.aliases.progx.enabled=true +adapters.teqblaze.aliases.progx.endpoint=http://localhost:8090/progx-exchange/ adapters.theadx.enabled=true adapters.theadx.endpoint=http://localhost:8090/theadx-exchange adapters.tradplus.enabled=true @@ -637,8 +641,6 @@ adapters.xeworks.aliases.adipolo.enabled=true adapters.xeworks.aliases.adipolo.endpoint=http://localhost:8090/adipolo-exchange adapters.vidazoo.enabled=true adapters.vidazoo.endpoint=http://localhost:8090/vidazoo-exchange/ -adapters.vidazoo.aliases.progx.enabled=true -adapters.vidazoo.aliases.progx.endpoint=http://localhost:8090/progx-exchange/ adapters.vidazoo.aliases.omnidex.enabled=true adapters.vidazoo.aliases.omnidex.endpoint=http://localhost:8090/omnidex-exchange/ adapters.vidazoo.aliases.tagoras.enabled=true