Skip to content

Commit 4ebd410

Browse files
authored
Seedtag: adds ron id support for seedtag adapter (#4466)
1 parent 4cab9bd commit 4ebd410

5 files changed

Lines changed: 166 additions & 4 deletions

File tree

src/main/java/org/prebid/server/bidder/seedtag/SeedtagBidder.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.iab.openrtb.response.BidResponse;
88
import com.iab.openrtb.response.SeatBid;
99
import org.apache.commons.collections4.CollectionUtils;
10+
import org.apache.commons.lang3.StringUtils;
1011
import org.prebid.server.bidder.Bidder;
1112
import org.prebid.server.bidder.model.BidderBid;
1213
import org.prebid.server.bidder.model.BidderCall;
@@ -37,6 +38,7 @@ public class SeedtagBidder implements Bidder<BidRequest> {
3738
new TypeReference<>() {
3839
};
3940
private static final String BIDDER_CURRENCY = "USD";
41+
private static final String INTEGRATION_TYPE_RON_ID = "ronId";
4042

4143
private final String endpointUrl;
4244
private final JacksonMapper mapper;
@@ -58,8 +60,8 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
5860

5961
for (Imp imp : request.getImp()) {
6062
try {
63+
validateImpExt(imp);
6164
final Price bidFloorPrice = resolveBidFloor(imp, request);
62-
6365
modifiedImps.add(modifyImp(imp, bidFloorPrice));
6466
} catch (PreBidException e) {
6567
errors.add(BidderError.badInput(e.getMessage()));
@@ -79,6 +81,29 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
7981
errors);
8082
}
8183

84+
private void validateImpExt(Imp imp) {
85+
final ExtImpSeedtag ext;
86+
try {
87+
ext = mapper.mapper().convertValue(imp.getExt(), SEEDTAG_EXT_TYPE_REFERENCE).getBidder();
88+
} catch (Exception e) {
89+
throw new PreBidException("Invalid imp.ext.bidder for imp id: %s".formatted(imp.getId()));
90+
}
91+
92+
if (INTEGRATION_TYPE_RON_ID.equals(ext.getIntegrationType())) {
93+
if (StringUtils.isBlank(ext.getPublisherId())) {
94+
throw new PreBidException(
95+
"imp id %s: publisherId is required when integrationType is '%s'"
96+
.formatted(imp.getId(), INTEGRATION_TYPE_RON_ID));
97+
}
98+
} else {
99+
if (StringUtils.isBlank(ext.getAdUnitId())) {
100+
throw new PreBidException(
101+
"imp id %s: adUnitId is required when integrationType is not '%s'"
102+
.formatted(imp.getId(), INTEGRATION_TYPE_RON_ID));
103+
}
104+
}
105+
}
106+
82107
private static Imp modifyImp(Imp imp, Price bidFloorPrice) {
83108
return imp.toBuilder()
84109
.bidfloorcur(bidFloorPrice.getCurrency())

src/main/java/org/prebid/server/proto/openrtb/ext/request/seedtag/ExtImpSeedtag.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,10 @@ public class ExtImpSeedtag {
99
@JsonProperty("adUnitId")
1010
String adUnitId;
1111

12+
@JsonProperty("publisherId")
13+
String publisherId;
14+
15+
@JsonProperty("integrationType")
16+
String integrationType;
17+
1218
}

src/main/resources/static/bidder-params/seedtag.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,20 @@
88
"type": "string",
99
"description": "Ad Unit ID",
1010
"minLength": 1
11+
},
12+
"publisherId": {
13+
"type": "string",
14+
"description": "Publisher ID (editorial group ID)",
15+
"minLength": 1
16+
},
17+
"integrationType": {
18+
"type": "string",
19+
"description": "Integration type",
20+
"enum": ["ronId"]
1121
}
1222
},
13-
"required": [
14-
"adUnitId"
23+
"oneOf": [
24+
{ "required": ["adUnitId"] },
25+
{ "required": ["publisherId", "integrationType"] }
1526
]
1627
}

src/test/java/org/prebid/server/bidder/seedtag/SeedtagBidderTest.java

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.prebid.server.bidder.seedtag;
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.node.ObjectNode;
45
import com.iab.openrtb.request.BidRequest;
56
import com.iab.openrtb.request.Imp;
67
import com.iab.openrtb.response.Bid;
@@ -20,6 +21,7 @@
2021
import org.prebid.server.bidder.model.Result;
2122
import org.prebid.server.currency.CurrencyConversionService;
2223
import org.prebid.server.exception.PreBidException;
24+
import org.prebid.server.proto.openrtb.ext.request.seedtag.ExtImpSeedtag;
2325

2426
import java.math.BigDecimal;
2527
import java.util.List;
@@ -127,6 +129,115 @@ public void makeHttpRequestsShouldSkipImpsWithCurrencyThatCanNotBeConverted() {
127129
.hasSize(1);
128130
}
129131

132+
@Test
133+
public void makeHttpRequestsShouldSucceedWithPublisherIdAndRonIdIntegrationType() {
134+
// given
135+
final BidRequest bidRequest = givenBidRequest(
136+
identity(),
137+
requestBuilder -> requestBuilder.imp(singletonList(
138+
givenImp(ExtImpSeedtag.of(null, "somePubId", "ronId")))));
139+
140+
// when
141+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
142+
143+
// then
144+
assertThat(result.getErrors()).isEmpty();
145+
assertThat(result.getValue()).hasSize(1);
146+
assertThat(result.getValue().get(0).getPayload().getImp()).hasSize(1);
147+
}
148+
149+
@Test
150+
public void makeHttpRequestsShouldSucceedWithAdUnitIdAndPublisherIdWhenIntegrationTypeIsRonId() {
151+
// given: adUnitId is irrelevant when integrationType is ronId — publisherId is what matters
152+
final BidRequest bidRequest = givenBidRequest(
153+
identity(),
154+
requestBuilder -> requestBuilder.imp(singletonList(
155+
givenImp(ExtImpSeedtag.of("someAdUnitId", "somePubId", "ronId")))));
156+
157+
// when
158+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
159+
160+
// then
161+
assertThat(result.getErrors()).isEmpty();
162+
assertThat(result.getValue()).hasSize(1);
163+
}
164+
165+
@Test
166+
public void makeHttpRequestsShouldSucceedWithAdUnitIdWhenIntegrationTypeIsAbsent() {
167+
// given
168+
final BidRequest bidRequest = givenBidRequest(
169+
identity(),
170+
requestBuilder -> requestBuilder.imp(singletonList(
171+
givenImp(ExtImpSeedtag.of("someAdUnitId", null, null)))));
172+
173+
// when
174+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
175+
176+
// then
177+
assertThat(result.getErrors()).isEmpty();
178+
assertThat(result.getValue()).hasSize(1);
179+
}
180+
181+
@Test
182+
public void makeHttpRequestsShouldSkipImpWithRonIdIntegrationTypeButMissingPublisherId() {
183+
// given
184+
final BidRequest bidRequest = givenBidRequest(
185+
identity(),
186+
requestBuilder -> requestBuilder.imp(singletonList(
187+
givenImp(ExtImpSeedtag.of("someAdUnitId", null, "ronId")))));
188+
189+
// when
190+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
191+
192+
// then
193+
assertThat(result.getValue()).isEmpty();
194+
assertThat(result.getErrors()).hasSize(1)
195+
.allSatisfy(error -> {
196+
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
197+
assertThat(error.getMessage()).contains("publisherId is required when integrationType is 'ronId'");
198+
});
199+
}
200+
201+
@Test
202+
public void makeHttpRequestsShouldSkipImpWithNoAdUnitIdAndNoRonIdIntegrationType() {
203+
// given: no adUnitId and integrationType is not ronId → adUnitId is required
204+
final BidRequest bidRequest = givenBidRequest(
205+
identity(),
206+
requestBuilder -> requestBuilder.imp(singletonList(
207+
givenImp(ExtImpSeedtag.of(null, "somePubId", null)))));
208+
209+
// when
210+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
211+
212+
// then
213+
assertThat(result.getValue()).isEmpty();
214+
assertThat(result.getErrors()).hasSize(1)
215+
.allSatisfy(error -> {
216+
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
217+
assertThat(error.getMessage()).contains("adUnitId is required when integrationType is not 'ronId'");
218+
});
219+
}
220+
221+
@Test
222+
public void makeHttpRequestsShouldSkipImpWithNoAdUnitIdAndNoParams() {
223+
// given
224+
final BidRequest bidRequest = givenBidRequest(
225+
identity(),
226+
requestBuilder -> requestBuilder.imp(singletonList(
227+
givenImp(ExtImpSeedtag.of(null, null, null)))));
228+
229+
// when
230+
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);
231+
232+
// then
233+
assertThat(result.getValue()).isEmpty();
234+
assertThat(result.getErrors()).hasSize(1)
235+
.allSatisfy(error -> {
236+
assertThat(error.getType()).isEqualTo(BidderError.Type.bad_input);
237+
assertThat(error.getMessage()).contains("adUnitId is required when integrationType is not 'ronId'");
238+
});
239+
}
240+
130241
@Test
131242
public void makeBidsShouldReturnEmptyListIfBidResponseIsNull() throws JsonProcessingException {
132243
// given
@@ -231,7 +342,15 @@ private static BidRequest givenBidRequest(UnaryOperator<Imp.ImpBuilder> impCusto
231342
}
232343

233344
private static Imp givenImp(UnaryOperator<Imp.ImpBuilder> impCustomizer) {
234-
return impCustomizer.apply(Imp.builder().id("123")).build();
345+
final ObjectNode bidderExt = mapper.createObjectNode();
346+
bidderExt.set("bidder", mapper.valueToTree(ExtImpSeedtag.of("someAdUnitId", null, null)));
347+
return impCustomizer.apply(Imp.builder().id("123").ext(bidderExt)).build();
348+
}
349+
350+
private static Imp givenImp(ExtImpSeedtag extImpSeedtag) {
351+
final ObjectNode bidderExt = mapper.createObjectNode();
352+
bidderExt.set("bidder", mapper.valueToTree(extImpSeedtag));
353+
return Imp.builder().id("123").ext(bidderExt).build();
235354
}
236355

237356
private static BidResponse givenBidResponse(UnaryOperator<Bid.BidBuilder> bidCustomizer) {

src/test/java/org/prebid/server/it/SeedtagTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ public void openrtb2AuctionShouldRespondWithBidsFromSeedtag() throws IOException
3030
assertJsonEquals("openrtb2/seedtag/test-auction-seedtag-response.json", response,
3131
singletonList("seedtag"));
3232
}
33+
3334
}

0 commit comments

Comments
 (0)