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

Implement flag to allow VAST modification for unknown bidders #1419

Merged
merged 4 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ Removes and downloads file again if depending service cant process probably corr
- `cookie-sync.coop-sync.pri` - lists of bidders prioritised in groups.

## Vtrack
- `vtrack.allow-unkonwn-bidder` - flag allows servicing requests with bidders who were not configured in Prebid Server.
- `vtrack.allow-unknown-bidder` - flag that allows servicing requests with bidders who were not configured in Prebid Server.
- `vtrack.modify-vast-for-unknown-bidder` - flag that allows modifying the VAST value and adding the impression tag to it, for bidders who were not configured in Prebid Server.

## Adapters
- `adapters.*` - the section for bidder specific configuration options.
Expand Down
34 changes: 27 additions & 7 deletions src/main/java/org/prebid/server/handler/VtrackHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prebid.server.handler;

import com.fasterxml.jackson.databind.JsonNode;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.AsyncResult;
Expand Down Expand Up @@ -39,9 +40,11 @@ public class VtrackHandler implements Handler<RoutingContext> {

private static final String ACCOUNT_PARAMETER = "a";
private static final String INTEGRATION_PARAMETER = "int";
private static final String TYPE_XML = "xml";

private final long defaultTimeout;
private final boolean allowUnknownBidder;
private final boolean modifyVastForUnknownBidder;
private final ApplicationSettings applicationSettings;
private final BidderCatalog bidderCatalog;
private final CacheService cacheService;
Expand All @@ -50,6 +53,7 @@ public class VtrackHandler implements Handler<RoutingContext> {

public VtrackHandler(long defaultTimeout,
boolean allowUnknownBidder,
boolean modifyVastForUnknownBidder,
ApplicationSettings applicationSettings,
BidderCatalog bidderCatalog,
CacheService cacheService,
Expand All @@ -58,6 +62,7 @@ public VtrackHandler(long defaultTimeout,

this.defaultTimeout = defaultTimeout;
this.allowUnknownBidder = allowUnknownBidder;
this.modifyVastForUnknownBidder = modifyVastForUnknownBidder;
this.applicationSettings = Objects.requireNonNull(applicationSettings);
this.bidderCatalog = Objects.requireNonNull(bidderCatalog);
this.cacheService = Objects.requireNonNull(cacheService);
Expand Down Expand Up @@ -110,16 +115,31 @@ private List<PutObject> vtrackPuts(RoutingContext routingContext) {

final List<PutObject> putObjects = ListUtils.emptyIfNull(bidCacheRequest.getPuts());
for (PutObject putObject : putObjects) {
if (StringUtils.isEmpty(putObject.getBidid())) {
throw new IllegalArgumentException("'bidid' is required field and can't be empty");
}
if (StringUtils.isEmpty(putObject.getBidder())) {
throw new IllegalArgumentException("'bidder' is required field and can't be empty");
}
validatePutObject(putObject);
}
return putObjects;
}

private static void validatePutObject(PutObject putObject) {
if (StringUtils.isEmpty(putObject.getBidid())) {
throw new IllegalArgumentException("'bidid' is required field and can't be empty");
}

if (StringUtils.isEmpty(putObject.getBidder())) {
throw new IllegalArgumentException("'bidder' is required field and can't be empty");
}

if (!StringUtils.equals(putObject.getType(), TYPE_XML)) {
throw new IllegalArgumentException("vtrack only accepts type xml");
}

final JsonNode value = putObject.getValue();
final String valueAsString = value != null ? value.asText() : null;
if (!StringUtils.containsIgnoreCase(valueAsString, "<vast")) {
throw new IllegalArgumentException("vtrack content must be vast");
}
}

public static String integration(RoutingContext routingContext) {
EventUtil.validateIntegration(routingContext);
return routingContext.request().getParam(INTEGRATION_PARAMETER);
Expand Down Expand Up @@ -167,7 +187,7 @@ private boolean isAllowVastForBidder(String bidderName) {
if (bidderCatalog.isValidName(bidderName)) {
return bidderCatalog.isModifyingVastXmlAllowed(bidderName);
} else {
return allowUnknownBidder;
return allowUnknownBidder && modifyVastForUnknownBidder;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ GetuidsHandler getuidsHandler(UidsCookieService uidsCookieService, JacksonMapper
@Bean
VtrackHandler vtrackHandler(
@Value("${vtrack.default-timeout-ms}") int defaultTimeoutMs,
@Value("${vtrack.allow-unkonwn-bidder}") boolean allowUnknownBidder,
@Value("${vtrack.allow-unknown-bidder}") boolean allowUnknownBidder,
@Value("${vtrack.modify-vast-for-unknown-bidder}") boolean modifyVastForUnknownBidder,
ApplicationSettings applicationSettings,
BidderCatalog bidderCatalog,
CacheService cacheService,
Expand All @@ -341,6 +342,7 @@ VtrackHandler vtrackHandler(
return new VtrackHandler(
defaultTimeoutMs,
allowUnknownBidder,
modifyVastForUnknownBidder,
applicationSettings,
bidderCatalog,
cacheService,
Expand Down
31 changes: 13 additions & 18 deletions src/main/java/org/prebid/server/vast/VastModifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
public class VastModifier {

private static final String IN_LINE_TAG = "<InLine>";
private static final String IN_LINE_CLOSE_TAG = "</InLine>";
private static final String WRAPPER_TAG = "<Wrapper>";
private static final String WRAPPER_CLOSE_TAG = "</Wrapper>";
private static final String IMPRESSION_CLOSE_TAG = "</Impression>";
Expand Down Expand Up @@ -102,30 +103,19 @@ private String appendTrackingUrlToVastXml(String vastXml, String vastUrlTracking
final int wrapperTagIndex = StringUtils.indexOfIgnoreCase(vastXml, WRAPPER_TAG);

if (inLineTagIndex != -1) {
return appendTrackingUrlForInlineType(vastXml, vastUrlTracking);
return appendTrackingUrl(vastXml, vastUrlTracking, IN_LINE_CLOSE_TAG);
} else if (wrapperTagIndex != -1) {
return appendTrackingUrl(vastXml, vastUrlTracking);
return appendTrackingUrl(vastXml, vastUrlTracking, WRAPPER_CLOSE_TAG);
}
throw new PreBidException(
String.format("VastXml does not contain neither InLine nor Wrapper for %s response", bidder));
}

private static String appendTrackingUrlForInlineType(String vastXml, String vastUrlTracking) {
final int closeTagIndex = vastXml.indexOf(IMPRESSION_CLOSE_TAG);

// no impression tag - pass it as it is
if (closeTagIndex == -1) {
return vastXml;
}

return appendTrackingUrl(vastXml, vastUrlTracking);
}

private static String appendTrackingUrl(String vastXml, String vastUrlTracking) {
private static String appendTrackingUrl(String vastXml, String vastUrlTracking, String elementCloseTag) {
if (vastXml.contains(IMPRESSION_CLOSE_TAG)) {
return insertAfterExistingImpressionTag(vastXml, vastUrlTracking);
}
return insertBeforeEndOfWrapperElement(vastXml, vastUrlTracking);
return insertBeforeElementCloseTag(vastXml, vastUrlTracking, elementCloseTag);
}

private static String insertAfterExistingImpressionTag(String vastXml, String vastUrlTracking) {
Expand All @@ -139,10 +129,15 @@ private static String insertAfterExistingImpressionTag(String vastXml, String va
.toString();
}

private static String insertBeforeEndOfWrapperElement(String vastXml, String vastUrlTracking) {
final int indexOfCloseTag = StringUtils.indexOfIgnoreCase(vastXml, WRAPPER_CLOSE_TAG);
private static String insertBeforeElementCloseTag(String vastXml, String vastUrlTracking, String elementCloseTag) {
final int indexOfCloseTag = StringUtils.indexOfIgnoreCase(vastXml, elementCloseTag);

if (indexOfCloseTag == -1) {
return vastXml;
}

final String caseSpecificCloseTag =
vastXml.substring(indexOfCloseTag, indexOfCloseTag + WRAPPER_CLOSE_TAG.length());
vastXml.substring(indexOfCloseTag, indexOfCloseTag + elementCloseTag.length());
final String impressionTag = "<Impression><![CDATA[" + vastUrlTracking + "]]></Impression>";

return vastXml.replace(caseSpecificCloseTag, impressionTag + caseSpecificCloseTag);
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ setuid:
default-timeout-ms: 2000
vtrack:
default-timeout-ms: 2000
allow-unkonwn-bidder: true
allow-unknown-bidder: true
modify-vast-for-unknown-bidder: true
cookie-sync:
coop-sync:
default: true
Expand Down
Loading