Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add client/AccountID support into Adoppler adapter #1010

Merged
merged 5 commits into from
Nov 19, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions src/main/java/org/prebid/server/bidder/adoppler/AdopplerBidder.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class AdopplerBidder implements Bidder<BidRequest> {
private static final TypeReference<ExtPrebid<?, ExtImpAdoppler>> ADOPPLER_EXT_TYPE_REFERENCE =
new TypeReference<ExtPrebid<?, ExtImpAdoppler>>() {
};
private static final String DEFAULT_CLIENT = "app";

private final String endpointTemplate;
private final JacksonMapper mapper;
Expand All @@ -63,9 +64,9 @@ public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request
final ExtImpAdoppler validExtImp = parseAndValidateImpExt(imp);
final String updateRequestId = request.getId() + "-" + validExtImp.getAdunit();
final BidRequest updateRequest = request.toBuilder().id(updateRequestId).build();
final String uri = String.format("%s/processHeaderBid/%s", endpointTemplate,
HttpUtil.encodeUrl(validExtImp.getAdunit()));
result.add(createSingleRequest(imp, updateRequest, uri));
final String url = resolveUrl(validExtImp);

result.add(createSingleRequest(imp, updateRequest, url));
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
}
Expand All @@ -78,14 +79,33 @@ private ExtImpAdoppler parseAndValidateImpExt(Imp imp) {
try {
extImpAdoppler = mapper.mapper().convertValue(imp.getExt(), ADOPPLER_EXT_TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage(), e);
throw new PreBidException(e.getMessage());
}
if (StringUtils.isBlank(extImpAdoppler.getAdunit())) {
throw new PreBidException("$.imp.ext.adoppler.adunit required");
}
return extImpAdoppler;
}

private String resolveUrl(ExtImpAdoppler extImp) {
final String accountIdMacro;
final String adUnitMacro;
final String client = extImp.getClient();

try {
accountIdMacro = StringUtils.isBlank(client)
? DEFAULT_CLIENT
: HttpUtil.encodeUrl(client);
adUnitMacro = HttpUtil.encodeUrl(extImp.getAdunit());
} catch (Exception e) {
throw new PreBidException(e.getMessage());
}

return endpointTemplate
.replace("{{AccountID}}", accountIdMacro)
.replace("{{AdUnit}}", adUnitMacro);
}

SerhiiNahornyi marked this conversation as resolved.
Show resolved Hide resolved
private HttpRequest<BidRequest> createSingleRequest(Imp imp, BidRequest request, String url) {
final BidRequest outgoingRequest = request.toBuilder().imp(Collections.singletonList(imp)).build();
final String body = mapper.encode(outgoingRequest);
Expand Down Expand Up @@ -121,24 +141,25 @@ private BidResponse decodeBodyToBidResponse(HttpCall<BidRequest> httpCall) {
try {
return mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
} catch (DecodeException e) {
throw new PreBidException(e.getMessage(), e);
throw new PreBidException(String.format("invalid body: %s", e.getMessage()));
}
}

private Map<String, BidType> getImpTypes(BidRequest bidRequest) {
final Map<String, BidType> impTypes = new HashMap<>();
for (Imp imp : bidRequest.getImp()) {
if (impTypes.get(imp.getId()) != null) {
throw new PreBidException(String.format("duplicate $.imp.id %s", imp.getId()));
final String impId = imp.getId();
if (impTypes.get(impId) != null) {
throw new PreBidException(String.format("duplicate $.imp.id %s", impId));
}
if (imp.getBanner() != null) {
impTypes.put(imp.getId(), BidType.banner);
impTypes.put(impId, BidType.banner);
} else if (imp.getVideo() != null) {
impTypes.put(imp.getId(), BidType.video);
impTypes.put(impId, BidType.video);
} else if (imp.getAudio() != null) {
impTypes.put(imp.getId(), BidType.audio);
impTypes.put(impId, BidType.audio);
} else if (imp.getXNative() != null) {
impTypes.put(imp.getId(), BidType.xNative);
impTypes.put(impId, BidType.xNative);
} else {
throw new PreBidException("one of $.imp.banner, $.imp.video, $.imp.audio "
+ "and $.imp.native field required");
Expand All @@ -149,7 +170,7 @@ private Map<String, BidType> getImpTypes(BidRequest bidRequest) {

private BidderBid createBid(Bid bid, Map<String, BidType> impTypes, String currency) {
if (impTypes.get(bid.getImpid()) == null) {
throw new PreBidException(String.format("unknown impid: %s", bid.getImpid()));
throw new PreBidException(String.format("unknown impId: %s", bid.getImpid()));
}
validateResponseVideoExt(bid, impTypes);
return BidderBid.of(bid, impTypes.get(bid.getImpid()), currency);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
public class ExtImpAdoppler {

String adunit;
String client;
}
4 changes: 2 additions & 2 deletions src/main/resources/bidder-config/adoppler.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
adapters:
adoppler:
enabled: false
endpoint: http://app.trustedmarketplace.io/ads
endpoint: http://{{AccountID}}.trustedmarketplace.io/ads/processHeaderBid/{{AdUnit}}
pbs-enforces-gdpr: true
pbs-enforces-ccpa: true
modifying-vast-xml-allowed: true
Expand All @@ -22,4 +22,4 @@ adapters:
redirect-url:
cookie-family-name: adoppler
type: redirect
support-cors: false
support-cors: false
4 changes: 4 additions & 0 deletions src/main/resources/static/bidder-params/adoppler.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"adunit": {
"type": "string",
"description": "AdUnit to bid against to."
},
"client": {
"type": "string",
"description": "Client name."
}
},
"required": ["adunit"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.Banner;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Video;
import com.iab.openrtb.response.Bid;
Expand All @@ -25,7 +24,7 @@
import org.prebid.server.proto.openrtb.ext.request.adoppler.ExtImpAdoppler;
import org.prebid.server.util.HttpUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand All @@ -39,7 +38,7 @@

public class AdopplerBidderTest extends VertxTest {

private static final String ENDPOINT_URL = "https://test.endpoint.com";
private static final String ENDPOINT_URL = "http://{{AccountID}}.test.com/some/path/{{AdUnit}}";

private AdopplerBidder adopplerBidder;

Expand All @@ -58,38 +57,47 @@ public void makeHttpRequestsShouldReturnErrorIfImpExtCouldNotBeParsed() {
// given
final BidRequest bidRequest = givenBidRequest(
impBuilder -> impBuilder
.banner(Banner.builder()
.format(singletonList(Format.builder().w(300).h(500).build()))
.build())
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of(null)))));
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of(null, null)))));
// when
final Result<List<HttpRequest<BidRequest>>> result = adopplerBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).hasSize(1);
assertThat(result.getErrors().get(0).getMessage()).startsWith("$.imp.ext.adoppler.adunit required");
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("$.imp.ext.adoppler.adunit required"));
}

@Test
public void makeHttpRequestsShouldCreateCorrectURL() {
// given
final BidRequest bidRequest = givenBidRequest(identity());

// when
final Result<List<HttpRequest<BidRequest>>> result = adopplerBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1);
assertThat(result.getValue().get(0).getUri()).isEqualTo("http://clientId.test.com/some/path/adUnit");
}

@Test
public void makeHttpRequestsShouldCreateUrlWithDefaultAppParamIfClientIsMissing() {
// given
final BidRequest bidRequest = givenBidRequest(
impBuilder -> impBuilder
.banner(Banner.builder()
.format(singletonList(Format.builder().w(300).h(500).build()))
.build()));
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit", "")))));

// when
final Result<List<HttpRequest<BidRequest>>> result = adopplerBidder.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue()).hasSize(1);
assertThat(result.getValue().get(0).getUri()).isEqualTo("https://test.endpoint.com/processHeaderBid/adUnit");
assertThat(result.getValue().get(0).getUri()).isEqualTo("http://app.test.com/some/path/adUnit");
}

@Test
public void makeHttpRequestsShouldSetExpectedRequestUrlAndDefaultHeaders() {
public void makeHttpRequestsShouldSetExpectedHeaders() {
// given
final BidRequest bidRequest = givenBidRequest(identity());

Expand All @@ -100,7 +108,7 @@ public void makeHttpRequestsShouldSetExpectedRequestUrlAndDefaultHeaders() {
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue().get(0).getHeaders()).isNotNull()
.extracting(Map.Entry::getKey, Map.Entry::getValue)
.containsOnly(tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"),
.containsExactlyInAnyOrder(tuple(HttpUtil.X_OPENRTB_VERSION_HEADER.toString(), "2.5"),
tuple(HttpUtil.CONTENT_TYPE_HEADER.toString(), HttpUtil.APPLICATION_JSON_CONTENT_TYPE),
tuple(HttpUtil.ACCEPT_HEADER.toString(), HttpHeaderValues.APPLICATION_JSON.toString()));
}
Expand All @@ -110,11 +118,9 @@ public void makeBidsShouldReturnErrorIfDuplicateId() throws JsonProcessingExcept
// given
final Imp imp1 = Imp.builder().id("impId").banner(Banner.builder().build()).build();
final Imp imp2 = Imp.builder().id("impId").video(Video.builder().build()).build();
final List imps = new ArrayList();
imps.add(imp1);
imps.add(imp2);
BidRequest bidRequest = BidRequest.builder()
.imp(imps)

final BidRequest bidRequest = BidRequest.builder()
.imp(Arrays.asList(imp1, imp2))
.build();
final HttpCall<BidRequest> httpCall = givenHttpCall(
bidRequest, mapper.writeValueAsString(
Expand All @@ -124,10 +130,8 @@ public void makeBidsShouldReturnErrorIfDuplicateId() throws JsonProcessingExcept
final Result<List<BidderBid>> result = adopplerBidder.makeBids(httpCall, bidRequest);

// then
assertThat(result.getErrors()).hasSize(1);
assertThat(result.getErrors().get(0).getMessage())
.startsWith("duplicate $.imp.id impId");
assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("duplicate $.imp.id impId"));
assertThat(result.getValue()).isEmpty();
}

Expand All @@ -150,10 +154,9 @@ public void makeBidsShouldReturnErrorIfEmptyImp() throws JsonProcessingException
final Result<List<BidderBid>> result = adopplerBidder.makeBids(httpCall, bidRequest);

// then
assertThat(result.getErrors()).hasSize(1);
assertThat(result.getErrors().get(0).getMessage())
.startsWith("one of $.imp.banner, $.imp.video, $.imp.audio and $.imp.native field required");
assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("one of $.imp.banner, $.imp.video, "
+ "$.imp.audio and $.imp.native field required"));
assertThat(result.getValue()).isEmpty();
}

Expand All @@ -173,19 +176,16 @@ public void makeBidsShouldReturnErrorIfBidIdEmpty() throws JsonProcessingExcepti
final Result<List<BidderBid>> result = adopplerBidder.makeBids(httpCall, bidRequest);

// then
assertThat(result.getErrors()).hasSize(1);
assertThat(result.getErrors().get(0).getMessage())
.startsWith("unknown impid: null");
assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("unknown impId: null"));
assertThat(result.getValue()).isEmpty();
}

@Test
public void makeBidsShouldReturnErrorIfExtEmpty() throws JsonProcessingException {
// given
final Imp imp = Imp.builder().id("impId").video(Video.builder().build()).build();
final List imps = Collections.singletonList(imp);
final BidRequest bidRequest = BidRequest.builder().imp(imps).build();
final BidRequest bidRequest = BidRequest.builder().imp(Collections.singletonList(imp)).build();
final ObjectNode ext = mapper.valueToTree(AdopplerResponseExt.of(null));
final HttpCall<BidRequest> httpCall = givenHttpCall(bidRequest, mapper.writeValueAsString(
givenBidResponse(bidBuilder -> bidBuilder
Expand All @@ -197,10 +197,8 @@ public void makeBidsShouldReturnErrorIfExtEmpty() throws JsonProcessingException
final Result<List<BidderBid>> result = adopplerBidder.makeBids(httpCall, bidRequest);

// then
assertThat(result.getErrors()).hasSize(1);
assertThat(result.getErrors().get(0).getMessage())
.startsWith("$.seatbid.bid.ext.ads.video required");
assertThat(result.getErrors().get(0).getType()).isEqualTo(BidderError.Type.bad_input);
assertThat(result.getErrors())
.containsExactly(BidderError.badInput("$.seatbid.bid.ext.ads.video required"));
assertThat(result.getValue()).isEmpty();
}

Expand All @@ -221,7 +219,7 @@ private static Imp givenImp(Function<Imp.ImpBuilder, Imp.ImpBuilder> impCustomiz
return impCustomizer.apply(Imp.builder()
.id("123")
.banner(Banner.builder().id("banner_id").build())
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit")))))
.ext(mapper.valueToTree(ExtPrebid.of(null, ExtImpAdoppler.of("adUnit", "clientId")))))
.build();
}

Expand Down
3 changes: 2 additions & 1 deletion src/test/java/org/prebid/server/it/AdopplerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ public class AdopplerTest extends IntegrationTest {
public void openrtb2AuctionShouldRespondWithBidsFromAdoppler() throws IOException, JSONException {
// given
// Adoppler bid response for imp 001
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adoppler-exchange/processHeaderBid/unit1"))
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/adoppler-exchange"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Content-Type", equalTo("application/json;charset=UTF-8"))
.withHeader("X-OpenRTB-Version", equalTo("2.5"))
.withRequestBody(equalToJson(jsonFrom("openrtb2/adoppler/test-adoppler-bid-request-1.json")))
.willReturn(aResponse().withBody(jsonFrom("openrtb2/adoppler/test-adoppler-bid-response-1.json"))));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"ext": {
"bidder": {
"adunit": "unit1"
"adunit": "unit1",
"client": "testClient"
}
}
}
Expand Down Expand Up @@ -84,4 +85,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"ext": {
"adoppler": {
"adunit": "unit1"
"adunit": "unit1",
"client": "testClient"
}
}
}
Expand Down Expand Up @@ -81,4 +82,4 @@
"gdpr": 0
}
}
}
}