From db42223819c8334d8f0849f99d2a10a31318f2e5 Mon Sep 17 00:00:00 2001 From: Viktor Kryshtal <33136089+Lightwood13@users.noreply.github.com> Date: Wed, 4 Feb 2026 15:29:22 +0200 Subject: [PATCH 1/8] Nexx360: Preserve custom imp.ext fields and add vendor id (#4359) --- .../server/bidder/nexx360/Nexx360Bidder.java | 5 +++- src/main/resources/bidder-config/nexx360.yaml | 2 +- .../bidder/nexx360/Nexx360BidderTest.java | 25 +++++++++++++++++++ .../easybid/test-easybid-bid-request.json | 1 + .../nexx360/test-nexx360-bid-request.json | 1 + .../oneaccord/test-1accord-bid-request.json | 1 + .../prismassp/test-prismassp-bid-request.json | 1 + 7 files changed, 34 insertions(+), 2 deletions(-) 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/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/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/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/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" } From 4d9af22ecf0269c188ca69d33f7123ffc363ff96 Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:30:54 +0100 Subject: [PATCH 2/8] New Adapter: RadiantFusion - Attekmi alias (#4365) --- .../resources/bidder-config/smarthub.yaml | 3 + .../prebid/server/it/RadianfusionTest.java | 38 ++++++++++++ .../test-auction-radianfusion-request.json | 25 ++++++++ .../test-auction-radianfusion-response.json | 43 ++++++++++++++ .../test-radianfusion-bid-request.json | 58 +++++++++++++++++++ .../test-radianfusion-bid-response.json | 23 ++++++++ .../server/it/test-application.properties | 2 + 7 files changed, 192 insertions(+) create mode 100644 src/test/java/org/prebid/server/it/RadianfusionTest.java create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-response.json diff --git a/src/main/resources/bidder-config/smarthub.yaml b/src/main/resources/bidder-config/smarthub.yaml index 73a50212676..d9441f1c0f3 100644 --- a/src/main/resources/bidder-config/smarthub.yaml +++ b/src/main/resources/bidder-config/smarthub.yaml @@ -29,6 +29,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/test/java/org/prebid/server/it/RadianfusionTest.java b/src/test/java/org/prebid/server/it/RadianfusionTest.java new file mode 100644 index 00000000000..52b401de7b3 --- /dev/null +++ b/src/test/java/org/prebid/server/it/RadianfusionTest.java @@ -0,0 +1,38 @@ +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.equalTo; +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 RadianfusionTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromRadianfusion() throws IOException, JSONException { + // given + WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/radianfusion-exchange")) + .withQueryParam("host", equalTo("someUniquePartnerName")) + .withQueryParam("accountId", equalTo("someSeat")) + .withQueryParam("sourceId", equalTo("someToken")) + .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/radianfusion/test-auction-radianfusion-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/radianfusion/test-auction-radianfusion-response.json", + response, singletonList("radianfusion")); + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json new file mode 100644 index 00000000000..c4c23330095 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-request.json @@ -0,0 +1,25 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "radianfusion": { + "partnerName": "someUniquePartnerName", + "seat": "someSeat", + "token": "someToken" + } + } + } + ], + "tmax": 5000, + "regs": { + "ext": { + "gdpr": 0 + } + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json new file mode 100644 index 00000000000..701a84c994b --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-auction-radianfusion-response.json @@ -0,0 +1,43 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "exp": 1500, + "price": 3.33, + "adm": "adm001", + "adid": "adid001", + "cid": "cid001", + "crid": "crid001", + "w": 300, + "h": 250, + "ext": { + "mediaType": "video", + "origbidcpm": 3.33, + "prebid": { + "type": "video", + "meta": { + "adaptercode": "radianfusion" + } + } + } + } + ], + "seat": "radianfusion", + "group": 0 + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "radianfusion": "{{ radianfusion.response_time_ms }}" + }, + "prebid": { + "auctiontimestamp": 0 + }, + "tmaxrequest": 5000 + } +} diff --git a/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json new file mode 100644 index 00000000000..9342b97a4fa --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-request.json @@ -0,0 +1,58 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "secure": 1, + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "partnerName": "someUniquePartnerName", + "seat": "someSeat", + "token": "someToken" + } + } + } + ], + "source": { + "tid": "${json-unit.any-string}" + }, + "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" + ], + "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/radianfusion/test-radianfusion-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-response.json new file mode 100644 index 00000000000..ecfbbed0ded --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/radianfusion/test-radianfusion-bid-response.json @@ -0,0 +1,23 @@ +{ + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 3.33, + "adid": "adid001", + "crid": "crid001", + "cid": "cid001", + "adm": "adm001", + "h": 250, + "w": 300, + "ext": { + "mediaType": "video" + } + } + ] + } + ] +} 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..07ea932405a 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -533,6 +533,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 From e1f5e96d6e4772bae3a6d308c0436c4f1d1cc632 Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:32:38 +0100 Subject: [PATCH 3/8] Remove Mobupps alias (#4364) --- src/main/resources/bidder-config/adverxo.yaml | 14 ----- .../org/prebid/server/it/MobuppsTest.java | 33 ------------ .../mobupps/test-auction-mobupps-request.json | 34 ------------- .../test-auction-mobupps-response.json | 43 ---------------- .../mobupps/test-mobupps-bid-request.json | 51 ------------------- .../mobupps/test-mobupps-bid-response.json | 40 --------------- .../server/it/test-application.properties | 2 - 7 files changed, 217 deletions(-) delete mode 100644 src/test/java/org/prebid/server/it/MobuppsTest.java delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-request.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-auction-mobupps-response.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-response.json 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/test/java/org/prebid/server/it/MobuppsTest.java b/src/test/java/org/prebid/server/it/MobuppsTest.java deleted file mode 100644 index 9987de46469..00000000000 --- a/src/test/java/org/prebid/server/it/MobuppsTest.java +++ /dev/null @@ -1,33 +0,0 @@ -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 MobuppsTest extends IntegrationTest { - - @Test - public void openrtb2AuctionShouldRespondWithBidsFromTheMobupps() 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")))); - - // when - final Response response = responseFor("openrtb2/mobupps/test-auction-mobupps-request.json", - Endpoint.openrtb2_auction); - - // then - assertJsonEquals("openrtb2/mobupps/test-auction-mobupps-response.json", response, - singletonList("mobupps")); - } -} 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-request.json b/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json deleted file mode 100644 index b1a0a5c0c8b..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/mobupps/test-mobupps-bid-request.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "id": "test-auction-request", - "imp": [ - { - "id": "imp1", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - } - ] - }, - "secure": 1, - "ext": { - "tid": "${json-unit.any-string}", - "bidder": { - "adUnitId": 1, - "auth": "123456" - } - } - } - ], - "site": { - "domain": "testpage.com", - "page": "http://testpage.com", - "publisher": { - "domain": "testpage.com" - }, - "ext": { - "amp": 0 - } - }, - "device": { - "ua": "Mozilla/5.0", - "ip": "193.168.244.1" - }, - "at": 1, - "tmax": "${json-unit.any-number}", - "cur": [ - "USD" - ], - "source": { - "tid": "${json-unit.any-string}" - }, - "regs": { - "ext": { - "gdpr": 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/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 07ea932405a..01458b652a2 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -76,8 +76,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 From 5ecd269707c878f0dfb70d6d73064076fb4fed4c Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:33:42 +0100 Subject: [PATCH 4/8] Remove Vimayx alias from SmartHub adapter (#4363) --- .../resources/bidder-config/smarthub.yaml | 3 - .../java/org/prebid/server/it/VimayxTest.java | 36 ------------ .../vimayx/test-auction-vimayx-request.json | 25 -------- .../vimayx/test-auction-vimayx-response.json | 43 -------------- .../vimayx/test-vimayx-bid-request.json | 58 ------------------- .../vimayx/test-vimayx-bid-response.json | 23 -------- .../server/it/test-application.properties | 2 - 7 files changed, 190 deletions(-) delete mode 100644 src/test/java/org/prebid/server/it/VimayxTest.java delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-request.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-response.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-request.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-response.json diff --git a/src/main/resources/bidder-config/smarthub.yaml b/src/main/resources/bidder-config/smarthub.yaml index d9441f1c0f3..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}} diff --git a/src/test/java/org/prebid/server/it/VimayxTest.java b/src/test/java/org/prebid/server/it/VimayxTest.java deleted file mode 100644 index ac7750065d0..00000000000 --- a/src/test/java/org/prebid/server/it/VimayxTest.java +++ /dev/null @@ -1,36 +0,0 @@ -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.equalTo; -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 VimayxTest extends IntegrationTest { - - @Test - public void openrtb2AuctionShouldRespondWithBidsFromVimayx() throws IOException, JSONException { - // given - WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/vimayx-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")))); - - // when - final Response response = responseFor("openrtb2/vimayx/test-auction-vimayx-request.json", - Endpoint.openrtb2_auction); - - // then - assertJsonEquals("openrtb2/vimayx/test-auction-vimayx-response.json", response, singletonList("vimayx")); - } -} 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/vimayx/test-auction-vimayx-request.json deleted file mode 100644 index 37f89d39672..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-request.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "id": "request_id", - "imp": [ - { - "id": "imp_id", - "banner": { - "w": 300, - "h": 250 - }, - "ext": { - "vimayx": { - "partnerName": "someUniquePartnerName", - "seat": "someSeat", - "token": "someToken" - } - } - } - ], - "tmax": 5000, - "regs": { - "ext": { - "gdpr": 0 - } - } -} 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/vimayx/test-auction-vimayx-response.json deleted file mode 100644 index 218b16cda60..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-auction-vimayx-response.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "id": "request_id", - "seatbid": [ - { - "bid": [ - { - "id": "bid_id", - "impid": "imp_id", - "exp": 1500, - "price": 3.33, - "adm": "adm001", - "adid": "adid001", - "cid": "cid001", - "crid": "crid001", - "w": 300, - "h": 250, - "ext": { - "mediaType": "video", - "origbidcpm": 3.33, - "prebid": { - "type": "video", - "meta": { - "adaptercode": "vimayx" - } - } - } - } - ], - "seat": "vimayx", - "group": 0 - } - ], - "cur": "USD", - "ext": { - "responsetimemillis": { - "vimayx": "{{ vimayx.response_time_ms }}" - }, - "prebid": { - "auctiontimestamp": 0 - }, - "tmaxrequest": 5000 - } -} 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/vimayx/test-vimayx-bid-request.json deleted file mode 100644 index 9342b97a4fa..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-request.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "id": "request_id", - "imp": [ - { - "id": "imp_id", - "secure": 1, - "banner": { - "w": 300, - "h": 250 - }, - "ext": { - "tid": "${json-unit.any-string}", - "bidder": { - "partnerName": "someUniquePartnerName", - "seat": "someSeat", - "token": "someToken" - } - } - } - ], - "source": { - "tid": "${json-unit.any-string}" - }, - "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" - ], - "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/vimayx/test-vimayx-bid-response.json b/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-response.json deleted file mode 100644 index ecfbbed0ded..00000000000 --- a/src/test/resources/org/prebid/server/it/openrtb2/vimayx/test-vimayx-bid-response.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": "request_id", - "seatbid": [ - { - "bid": [ - { - "id": "bid_id", - "impid": "imp_id", - "price": 3.33, - "adid": "adid001", - "crid": "crid001", - "cid": "cid001", - "adm": "adm001", - "h": 250, - "w": 300, - "ext": { - "mediaType": "video" - } - } - ] - } - ] -} 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 01458b652a2..41dc9fdb8ab 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -519,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 From 3934e336fb820f39397c970cd59c9ee68f7c3e1c Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:34:06 +0100 Subject: [PATCH 5/8] Migrate ProgX alias from Vidazoo to Teqblaze adapter (#4356) --- src/main/resources/bidder-config/teqblaze.yaml | 17 +++++++++++++++++ src/main/resources/bidder-config/vidazoo.yaml | 13 ------------- .../java/org/prebid/server/it/ProgxTest.java | 2 +- .../progx/test-auction-progx-request.json | 5 ++--- .../progx/test-auction-progx-response.json | 10 ++++------ .../openrtb2/progx/test-progx-bid-request.json | 16 +++++++++------- .../progx/test-progx-bid-response.json | 18 ++++++++++-------- .../server/it/test-application.properties | 4 ++-- 8 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/main/resources/bidder-config/teqblaze.yaml b/src/main/resources/bidder-config/teqblaze.yaml index 5e1006433ae..9938c49f107 100644 --- a/src/main/resources/bidder-config/teqblaze.yaml +++ b/src/main/resources/bidder-config/teqblaze.yaml @@ -43,6 +43,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/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/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/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index 41dc9fdb8ab..c6243333b71 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -583,6 +583,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 @@ -635,8 +637,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 From 6f8483d984d0822bcae05604ec284ceab2296c20 Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:34:18 +0100 Subject: [PATCH 6/8] AdOcean: Remove adapter (#4355) --- .../server/bidder/adocean/AdoceanBidder.java | 358 ----------- .../adocean/model/AdoceanResponseAdUnit.java | 32 - .../ext/request/adocean/ExtImpAdocean.java | 20 - .../config/bidder/AdoceanConfiguration.java | 41 -- src/main/resources/bidder-config/adocean.yaml | 11 - .../static/bidder-params/adocean.json | 44 -- .../bidder/adocean/AdoceanBidderTest.java | 572 ------------------ .../bidder/smarthub/SmarthubBidderTest.java | 3 +- .../org/prebid/server/it/AdoceanTest.java | 30 - .../adocean/test-adocean-bid-response-1.json | 14 - .../adocean/test-auction-adocean-request.json | 29 - .../test-auction-adocean-response.json | 41 -- .../server/it/test-application.properties | 2 - 13 files changed, 1 insertion(+), 1196 deletions(-) delete mode 100644 src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java delete mode 100644 src/main/java/org/prebid/server/bidder/adocean/model/AdoceanResponseAdUnit.java delete mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/request/adocean/ExtImpAdocean.java delete mode 100644 src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java delete mode 100644 src/main/resources/bidder-config/adocean.yaml delete mode 100644 src/main/resources/static/bidder-params/adocean.json delete mode 100644 src/test/java/org/prebid/server/bidder/adocean/AdoceanBidderTest.java delete mode 100644 src/test/java/org/prebid/server/it/AdoceanTest.java delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-adocean-bid-response-1.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-request.json delete mode 100644 src/test/resources/org/prebid/server/it/openrtb2/adocean/test-auction-adocean-response.json diff --git a/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java b/src/main/java/org/prebid/server/bidder/adocean/AdoceanBidder.java 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/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/spring/config/bidder/AdoceanConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java deleted file mode 100644 index d16984e3c5e..00000000000 --- a/src/main/java/org/prebid/server/spring/config/bidder/AdoceanConfiguration.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.prebid.server.spring.config.bidder; - -import org.prebid.server.bidder.BidderDeps; -import org.prebid.server.bidder.adocean.AdoceanBidder; -import org.prebid.server.json.JacksonMapper; -import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties; -import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler; -import org.prebid.server.spring.config.bidder.util.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/adocean.yaml", factory = YamlPropertySourceFactory.class) -public class AdoceanConfiguration { - - private static final String BIDDER_NAME = "adocean"; - - @Bean("adoceanConfigurationProperties") - @ConfigurationProperties("adapters.adocean") - BidderConfigurationProperties configurationProperties() { - return new BidderConfigurationProperties(); - } - - @Bean - BidderDeps adoceanBidderDeps(BidderConfigurationProperties adoceanConfigurationProperties, - @NotBlank @Value("${external-url}") String externalUrl, - JacksonMapper mapper) { - - return BidderDepsAssembler.forBidder(BIDDER_NAME) - .withConfig(adoceanConfigurationProperties) - .usersyncerCreator(UsersyncerCreator.create(externalUrl)) - .bidderCreator(config -> new AdoceanBidder(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/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/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/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/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/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/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index c6243333b71..f58858f44f2 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 From db7a3ae0c5a0d673d2abe479ceb3ba12ae4628a9 Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:34:32 +0100 Subject: [PATCH 7/8] Adot: Constrain publisher path to an enum list (#4354) --- src/main/resources/static/bidder-params/adot.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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": [] From 9e9bf59404062aa21738297c3c4af9a95958ea98 Mon Sep 17 00:00:00 2001 From: przemkaczmarek <167743744+przemkaczmarek@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:34:45 +0100 Subject: [PATCH 8/8] New Adapter: Clydo (#4299) --- .../server/bidder/clydo/ClydoBidder.java | 182 +++++++++ .../ext/request/clydo/ExtImpClydo.java | 13 + .../config/bidder/ClydoConfiguration.java | 41 ++ src/main/resources/bidder-config/clydo.yaml | 21 + .../resources/static/bidder-params/clydo.json | 20 + .../server/bidder/clydo/ClydoBidderTest.java | 383 ++++++++++++++++++ .../java/org/prebid/server/it/ClydoTest.java | 32 ++ .../clydo/test-auction-clydo-request.json | 24 ++ .../clydo/test-auction-clydo-response.json | 42 ++ .../clydo/test-clydo-bid-request.json | 57 +++ .../clydo/test-clydo-bid-response.json | 20 + .../server/it/test-application.properties | 2 + 12 files changed, 837 insertions(+) create mode 100644 src/main/java/org/prebid/server/bidder/clydo/ClydoBidder.java create mode 100644 src/main/java/org/prebid/server/proto/openrtb/ext/request/clydo/ExtImpClydo.java create mode 100644 src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java create mode 100644 src/main/resources/bidder-config/clydo.yaml create mode 100644 src/main/resources/static/bidder-params/clydo.json create mode 100644 src/test/java/org/prebid/server/bidder/clydo/ClydoBidderTest.java create mode 100644 src/test/java/org/prebid/server/it/ClydoTest.java create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/clydo/test-auction-clydo-response.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json create mode 100644 src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-response.json 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/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/spring/config/bidder/ClydoConfiguration.java b/src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java new file mode 100644 index 00000000000..98cedfc8d08 --- /dev/null +++ b/src/main/java/org/prebid/server/spring/config/bidder/ClydoConfiguration.java @@ -0,0 +1,41 @@ +package org.prebid.server.spring.config.bidder; + +import org.prebid.server.bidder.BidderDeps; +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; +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/clydo.yaml", factory = YamlPropertySourceFactory.class) +public class ClydoConfiguration { + + private static final String BIDDER_NAME = "clydo"; + + @Bean("clydoConfigurationProperties") + @ConfigurationProperties("adapters.clydo") + BidderConfigurationProperties configurationProperties() { + return new BidderConfigurationProperties(); + } + + @Bean + BidderDeps clydoBidderDeps(BidderConfigurationProperties clydoConfigurationProperties, + @NotBlank @Value("${external-url}") String externalUrl, + JacksonMapper mapper) { + + return BidderDepsAssembler.forBidder(BIDDER_NAME) + .withConfig(clydoConfigurationProperties) + .usersyncerCreator(UsersyncerCreator.create(externalUrl)) + .bidderCreator(config -> new ClydoBidder(config.getEndpoint(), mapper)) + .assemble(); + } +} 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/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/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/it/ClydoTest.java b/src/test/java/org/prebid/server/it/ClydoTest.java new file mode 100644 index 00000000000..6bb262350a3 --- /dev/null +++ b/src/test/java/org/prebid/server/it/ClydoTest.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 ClydoTest extends IntegrationTest { + + @Test + public void openrtb2AuctionShouldRespondWithBidsFromClydo() throws IOException, JSONException { + // given + 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/clydo/test-auction-clydo-request.json", + Endpoint.openrtb2_auction); + + // then + assertJsonEquals("openrtb2/clydo/test-auction-clydo-response.json", response, singletonList("clydo")); + } +} 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/clydo/test-clydo-bid-request.json b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json new file mode 100644 index 00000000000..e43823a0317 --- /dev/null +++ b/src/test/resources/org/prebid/server/it/openrtb2/clydo/test-clydo-bid-request.json @@ -0,0 +1,57 @@ +{ + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "secure": 1, + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "tid": "${json-unit.any-string}", + "bidder": { + "region": "us", + "partnerId": "testPartnerId" + } + } + } + ], + "source": { + "tid": "${json-unit.any-string}" + }, + "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" + ], + "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/test-application.properties b/src/test/resources/org/prebid/server/it/test-application.properties index f58858f44f2..439ef436125 100644 --- a/src/test/resources/org/prebid/server/it/test-application.properties +++ b/src/test/resources/org/prebid/server/it/test-application.properties @@ -187,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