diff --git a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java index 5770772c1bd..c91c23ffc88 100644 --- a/src/main/java/org/prebid/server/bidder/ix/IxBidder.java +++ b/src/main/java/org/prebid/server/bidder/ix/IxBidder.java @@ -68,11 +68,16 @@ public class IxBidder implements Bidder { private static final String PBS_VERSION_UNKNOWN = "unknown"; private final String endpointUrl; + private final String defaultAccountId; private final PrebidVersionProvider prebidVersionProvider; private final JacksonMapper mapper; - public IxBidder(String endpointUrl, PrebidVersionProvider prebidVersionProvider, JacksonMapper mapper) { + public IxBidder(String endpointUrl, + String defaultAccountId, + PrebidVersionProvider prebidVersionProvider, + JacksonMapper mapper) { this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl)); + this.defaultAccountId = (defaultAccountId != null) ? defaultAccountId : ""; this.prebidVersionProvider = Objects.requireNonNull(prebidVersionProvider); this.mapper = Objects.requireNonNull(mapper); } @@ -82,6 +87,7 @@ public Result>> makeHttpRequests(BidRequest bidRequ final Set siteIds = new HashSet<>(); final List imps = new ArrayList<>(); final List errors = new ArrayList<>(); + final Set accountIds = new HashSet<>(); for (Imp imp : bidRequest.getImp()) { try { @@ -91,6 +97,11 @@ public Result>> makeHttpRequests(BidRequest bidRequ siteIds.add(siteId); } + final String accountId = impExt.getAccountId(); + if (StringUtils.isNotEmpty(accountId)) { + accountIds.add(accountId); + } + imps.add(modifyImp(imp, impExt)); } catch (PreBidException e) { errors.add(BidderError.badInput(e.getMessage())); @@ -102,12 +113,24 @@ public Result>> makeHttpRequests(BidRequest bidRequ } final BidRequest modifiedBidRequest = modifyBidRequest(bidRequest, imps, siteIds); + + final String resolvedAccountId = accountIds.isEmpty() + ? null + : accountIds.iterator().next(); + + final String resolvedEndpoint = resolveEndpointMacro(endpointUrl, resolvedAccountId); + final List> httpRequests = Collections.singletonList( - BidderUtil.defaultRequest(modifiedBidRequest, endpointUrl, mapper)); + BidderUtil.defaultRequest(modifiedBidRequest, resolvedEndpoint, mapper)); return Result.of(httpRequests, errors); } + private String resolveEndpointMacro(String endpoint, String accountId) { + final String resolvedAccountId = StringUtils.isNotEmpty(accountId) ? accountId : this.defaultAccountId; + return endpoint.replace("${IX_ACCOUNT_ID}", resolvedAccountId); + } + private ExtImpIx parseImpExt(Imp imp) { try { return mapper.mapper().convertValue(imp.getExt(), IX_EXT_TYPE_REFERENCE).getBidder(); diff --git a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java index 09d49e9406a..699e22f2035 100644 --- a/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java +++ b/src/main/java/org/prebid/server/proto/openrtb/ext/request/ix/ExtImpIx.java @@ -16,4 +16,8 @@ public class ExtImpIx { List size; String sid; + + @JsonProperty("accountId") + @JsonAlias({"accountid", "ixAccountId"}) + String accountId; } diff --git a/src/main/java/org/prebid/server/spring/config/bidder/IxConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/IxConfiguration.java index 49a1bdbea69..3c147c83ee9 100644 --- a/src/main/java/org/prebid/server/spring/config/bidder/IxConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/bidder/IxConfiguration.java @@ -37,7 +37,11 @@ BidderDeps ixBidderDeps(BidderConfigurationProperties ixConfigurationProperties, return BidderDepsAssembler.forBidder(BIDDER_NAME) .withConfig(ixConfigurationProperties) .usersyncerCreator(UsersyncerCreator.create(externalUrl)) - .bidderCreator(config -> new IxBidder(config.getEndpoint(), prebidVersionProvider, mapper)) + .bidderCreator(config -> new IxBidder( + config.getEndpoint(), + config.getDefaultAccountId(), + prebidVersionProvider, + mapper)) .assemble(); } } diff --git a/src/main/java/org/prebid/server/spring/config/bidder/model/BidderConfigurationProperties.java b/src/main/java/org/prebid/server/spring/config/bidder/model/BidderConfigurationProperties.java index a13e1aef3ec..88bdd7b42d9 100644 --- a/src/main/java/org/prebid/server/spring/config/bidder/model/BidderConfigurationProperties.java +++ b/src/main/java/org/prebid/server/spring/config/bidder/model/BidderConfigurationProperties.java @@ -32,6 +32,8 @@ public class BidderConfigurationProperties { @NotBlank private String endpoint; + private String defaultAccountId; + private Boolean pbsEnforcesCcpa; private Boolean modifyingVastXmlAllowed; diff --git a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java index 051077ab03e..c909f4c0d37 100644 --- a/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/ix/IxBidderTest.java @@ -67,7 +67,8 @@ @ExtendWith(MockitoExtension.class) public class IxBidderTest extends VertxTest { - private static final String ENDPOINT_URL = "http://exchange.org/"; + private static final String ENDPOINT_URL = "http://exchange.org/?s=${IX_ACCOUNT_ID}"; + private static final String DEFAULT_ACCOUNT_ID = "123456"; private static final String SITE_ID = "site id"; @Mock(strictness = LENIENT) @@ -77,14 +78,14 @@ public class IxBidderTest extends VertxTest { @BeforeEach public void setUp() { - target = new IxBidder(ENDPOINT_URL, prebidVersionProvider, jacksonMapper); + target = new IxBidder(ENDPOINT_URL, DEFAULT_ACCOUNT_ID, prebidVersionProvider, jacksonMapper); given(prebidVersionProvider.getNameVersionRecord()).willReturn(null); } @Test public void creationShouldFailOnInvalidEndpointUrl() { assertThatIllegalArgumentException().isThrownBy( - () -> new IxBidder("invalid_url", prebidVersionProvider, jacksonMapper)); + () -> new IxBidder("invalid_url", DEFAULT_ACCOUNT_ID, prebidVersionProvider, jacksonMapper)); } @Test @@ -107,7 +108,7 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() { public void makeHttpRequestsShouldModifyImpExtWithSid() { // given final BidRequest bidRequest = givenBidRequest( - impBuilder -> impBuilder.ext(givenImpExt(null, "sid"))); + impBuilder -> impBuilder.ext(givenImpExt(null, "sid", null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -128,7 +129,7 @@ public void makeHttpRequestsShouldSetImpBannerFormatsToFormatWithWidthAndHeightI final BidRequest bidRequest = givenBidRequest( impBuilder -> impBuilder .banner(Banner.builder().w(1).h(2).build()) - .ext(givenImpExt(null, null))); + .ext(givenImpExt(null, null, null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -154,7 +155,7 @@ public void makeHttpRequestsShouldSetImpBannerWidthAndHeightIfTheyAreAbsentAndBa final BidRequest bidRequest = givenBidRequest( impBuilder -> impBuilder .banner(Banner.builder().format(singletonList(Format.builder().w(1).h(2).build())).build()) - .ext(givenImpExt(null, null))); + .ext(givenImpExt(null, null, null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -220,8 +221,8 @@ public void makeHttpRequestsShouldReturnIxDiagWithPbsVersion() { public void makeHttpRequestsShouldReturnIxDiagWithMultipleSiteIdsWhenMultipleImpExtSiteIdPresent() { // given final BidRequest bidRequest = givenBidRequest( - impBuilder -> impBuilder.ext(givenImpExt("site1", null)), - impBuilder -> impBuilder.ext(givenImpExt("site2", null))); + impBuilder -> impBuilder.ext(givenImpExt("site1", null, null)), + impBuilder -> impBuilder.ext(givenImpExt("site2", null, null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -255,8 +256,8 @@ public void makeHttpRequestsShouldPassThroughProvidedIxDiagFields() { "pbjsv1", givenIxDiag)), List.of( - impBuilder -> impBuilder.ext(givenImpExt("site1", null)), - impBuilder -> impBuilder.ext(givenImpExt("site2", null)))); + impBuilder -> impBuilder.ext(givenImpExt("site1", null, null)), + impBuilder -> impBuilder.ext(givenImpExt("site2", null, null)))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -284,7 +285,7 @@ public void makeHttpRequestsShouldModifyRequestSiteWithPublisherAndSetIdWhenImpE // given final BidRequest bidRequest = givenBidRequest( bidRequestBuilder -> bidRequestBuilder.site(Site.builder().build()), - singletonList(impBuilder -> impBuilder.ext(givenImpExt("site1", null)))); + singletonList(impBuilder -> impBuilder.ext(givenImpExt("site1", null, null)))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -304,7 +305,7 @@ public void makeHttpRequestsShouldModifyRequestSiteWithPublisherAndSetIdWhenImpE public void makeHttpRequestsShouldNotCreateRequestSiteWhenImpExtSiteIdPresentAndSiteIsAbsent() { // given final BidRequest bidRequest = givenBidRequest( - impBuilder -> impBuilder.ext(givenImpExt("site1", null))); + impBuilder -> impBuilder.ext(givenImpExt("site1", null, null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -322,7 +323,7 @@ public void makeHttpRequestsShouldModifyRequestAppWithPublisherAndSetIdWhenImpEx // given final BidRequest bidRequest = givenBidRequest( bidRequestBuilder -> bidRequestBuilder.app(App.builder().build()), - singletonList(impBuilder -> impBuilder.ext(givenImpExt("site1", null)))); + singletonList(impBuilder -> impBuilder.ext(givenImpExt("site1", null, null)))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -342,7 +343,7 @@ public void makeHttpRequestsShouldModifyRequestAppWithPublisherAndSetIdWhenImpEx public void makeHttpRequestsShouldNotCreateRequestAppWhenImpExtSiteIdPresentAndSiteIsAbsent() { // given final BidRequest bidRequest = givenBidRequest( - impBuilder -> impBuilder.ext(givenImpExt("site1", null))); + impBuilder -> impBuilder.ext(givenImpExt("site1", null, null))); // when final Result>> result = target.makeHttpRequests(bidRequest); @@ -355,6 +356,41 @@ public void makeHttpRequestsShouldNotCreateRequestAppWhenImpExtSiteIdPresentAndS .containsOnlyNulls(); } + @Test + public void makeHttpRequestsShouldReplaceAccountIdMacroInEndpointUrlWithEXTAccountId() { + // given + final String accountId = "account123"; + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder.ext(givenImpExt("site1", "sid", accountId))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1); + assertThat(result.getValue().get(0).getUri()) + .doesNotContain("${IX_ACCOUNT_ID}") + .contains(accountId); + } + + @Test + public void makeHttpRequestsShouldReplaceAccountIdMacroInEndpointUrlWithDefaultAccountId() { + // given + final BidRequest bidRequest = givenBidRequest( + impBuilder -> impBuilder.ext(givenImpExt("site1", "sid", null))); + + // when + final Result>> result = target.makeHttpRequests(bidRequest); + + // then + assertThat(result.getErrors()).isEmpty(); + assertThat(result.getValue()).hasSize(1); + assertThat(result.getValue().get(0).getUri()) + .doesNotContain("${IX_ACCOUNT_ID}") + .contains(DEFAULT_ACCOUNT_ID); + } + @Test public void makeBidderResponseShouldReturnErrorIfResponseBodyCouldNotBeParsed() { // given @@ -937,8 +973,8 @@ private static ExtRequest givenExtRequestWithIxDiag(String pbjsv, ObjectNode ixD return extRequest; } - private static ObjectNode givenImpExt(String siteId, String sid) { - return mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(siteId, null, sid))); + private static ObjectNode givenImpExt(String siteId, String sid, String accountId) { + return mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(siteId, null, sid, accountId))); } private static BidRequest givenBidRequest(UnaryOperator... impCustomizers) { @@ -961,7 +997,7 @@ private static Imp givenImp(Function impCustomiz return impCustomizer.apply(Imp.builder() .id("123") .banner(Banner.builder().w(1).h(1).build()) - .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(SITE_ID, null, null))))) + .ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpIx.of(SITE_ID, null, null, null))))) .build(); }