Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into core/analytic-call-per-account
Browse files Browse the repository at this point in the history
  • Loading branch information
Compile-Ninja committed Sep 19, 2024
2 parents 2dbc968 + f588358 commit ec4a599
Show file tree
Hide file tree
Showing 66 changed files with 2,471 additions and 180 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/docker-image-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,18 @@ jobs:
with:
images: ${{ matrix.package-name }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.dockerfile-path }}
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
5 changes: 5 additions & 0 deletions extra/bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
<artifactId>pb-richmedia-filter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>pb-response-correction</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
15 changes: 15 additions & 0 deletions extra/modules/pb-response-correction/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.prebid.server.hooks.modules</groupId>
<artifactId>all-modules</artifactId>
<version>3.12.0-SNAPSHOT</version>
</parent>

<artifactId>pb-response-correction</artifactId>

<name>pb-response-correction</name>
<description>Response correction module</description>
</project>
1 change: 1 addition & 0 deletions extra/modules/pb-response-correction/src/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lombok.anyConstructor.addConstructorProperties = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.prebid.server.hooks.modules.pb.response.correction.config;

import org.prebid.server.hooks.modules.pb.response.correction.core.correction.CorrectionProducer;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.appvideohtml.AppVideoHtmlCorrection;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.appvideohtml.AppVideoHtmlCorrectionProducer;
import org.prebid.server.hooks.modules.pb.response.correction.v1.ResponseCorrectionModule;
import org.prebid.server.hooks.modules.pb.response.correction.core.CorrectionsProvider;
import org.prebid.server.json.ObjectMapperProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@ConditionalOnProperty(prefix = "hooks." + ResponseCorrectionModule.CODE, name = "enabled", havingValue = "true")
@Configuration
public class ResponseCorrectionModuleConfiguration {

@Bean
AppVideoHtmlCorrectionProducer appVideoHtmlCorrectionProducer(
@Value("${logging.sampling-rate:0.01}") double logSamplingRate) {

return new AppVideoHtmlCorrectionProducer(
new AppVideoHtmlCorrection(ObjectMapperProvider.mapper(), logSamplingRate));
}

@Bean
CorrectionsProvider correctionsProvider(List<CorrectionProducer> correctionsProducers) {
return new CorrectionsProvider(correctionsProducers);
}

@Bean
ResponseCorrectionModule responseCorrectionModule(CorrectionsProvider correctionsProvider) {
return new ResponseCorrectionModule(correctionsProvider, ObjectMapperProvider.mapper());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.prebid.server.hooks.modules.pb.response.correction.core;

import com.iab.openrtb.request.BidRequest;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.Config;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.Correction;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.CorrectionProducer;

import java.util.List;
import java.util.Objects;

public class CorrectionsProvider {

private final List<CorrectionProducer> correctionsProducers;

public CorrectionsProvider(List<CorrectionProducer> correctionsProducers) {
this.correctionsProducers = Objects.requireNonNull(correctionsProducers);
}

public List<Correction> corrections(Config config, BidRequest bidRequest) {
return correctionsProducers.stream()
.filter(correctionProducer -> correctionProducer.shouldProduce(config, bidRequest))
.map(CorrectionProducer::produce)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.config.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;

import java.util.List;

@Value(staticConstructor = "of")
public class AppVideoHtmlConfig {

boolean enabled;

@JsonProperty("excluded-bidders")
List<String> excludedBidders;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.config.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;

@Value(staticConstructor = "of")
public class Config {

boolean enabled;

@JsonProperty("app-video-html")
AppVideoHtmlConfig appVideoHtmlConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.correction;

import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.Config;

import java.util.List;

public interface Correction {

List<BidderResponse> apply(Config config, List<BidderResponse> bidderResponses);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.correction;

import com.iab.openrtb.request.BidRequest;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.Config;

public interface CorrectionProducer {

boolean shouldProduce(Config config, BidRequest bidRequest);

Correction produce();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.correction.appvideohtml;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.response.Bid;
import org.apache.commons.collections4.CollectionUtils;
import org.prebid.server.auction.model.BidderResponse;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderSeatBid;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.Config;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.Correction;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.log.LoggerFactory;
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebidMeta;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;

public class AppVideoHtmlCorrection implements Correction {

private static final ConditionalLogger conditionalLogger = new ConditionalLogger(
LoggerFactory.getLogger(AppVideoHtmlCorrection.class));

private static final Pattern VAST_XML_PATTERN = Pattern.compile("<\\w*VAST\\w+", Pattern.CASE_INSENSITIVE);
private static final TypeReference<ExtPrebid<ExtBidPrebid, ObjectNode>> EXT_BID_PREBID_TYPE_REFERENCE =
new TypeReference<>() {
};

private static final String NATIVE_ADM_MESSAGE = "Bid %s of bidder %s has an JSON ADM, that appears to be native";
private static final String ADM_WITH_NO_ASSETS_MESSAGE = "Bid %s of bidder %s has a JSON ADM, but without assets";
private static final String CHANGING_BID_MEDIA_TYPE_MESSAGE = "Bid %s of bidder %s: changing media type to banner";

private final ObjectMapper mapper;
private final double logSamplingRate;

public AppVideoHtmlCorrection(ObjectMapper mapper, double logSamplingRate) {
this.mapper = mapper;
this.logSamplingRate = logSamplingRate;
}

@Override
public List<BidderResponse> apply(Config config, List<BidderResponse> bidderResponses) {
final Collection<String> excludedBidders = CollectionUtils.emptyIfNull(
config.getAppVideoHtmlConfig().getExcludedBidders());

return bidderResponses.stream()
.map(response -> modify(response, excludedBidders))
.toList();
}

private BidderResponse modify(BidderResponse response, Collection<String> excludedBidders) {
final String bidder = response.getBidder();
if (excludedBidders.contains(bidder)) {
return response;
}

final BidderSeatBid seatBid = response.getSeatBid();
final List<BidderBid> modifiedBids = seatBid.getBids().stream()
.map(bidderBid -> modifyBid(bidder, bidderBid))
.toList();

return response.with(seatBid.with(modifiedBids));
}

private BidderBid modifyBid(String bidder, BidderBid bidderBid) {
final Bid bid = bidderBid.getBid();
final String bidId = bid.getId();
final String adm = bid.getAdm();

if (adm == null || isVideoWithVastXml(bidderBid.getType(), adm) || hasNativeAdm(adm, bidId, bidder)) {
return bidderBid;
}

conditionalLogger.warn(CHANGING_BID_MEDIA_TYPE_MESSAGE.formatted(bidId, bidder), logSamplingRate);

final ExtBidPrebid prebid = parseExtBidPrebid(bid);

final ExtBidPrebidMeta modifiedMeta = Optional.ofNullable(prebid)
.map(ExtBidPrebid::getMeta)
.map(ExtBidPrebidMeta::toBuilder)
.orElseGet(ExtBidPrebidMeta::builder)
.mediaType(BidType.video.getName())
.build();

final ExtBidPrebid modifiedPrebid = Optional.ofNullable(prebid)
.map(ExtBidPrebid::toBuilder)
.orElseGet(ExtBidPrebid::builder)
.meta(modifiedMeta)
.type(BidType.banner)
.build();

final ObjectNode modifiedBidExt = mapper.valueToTree(ExtPrebid.of(modifiedPrebid, null));

return bidderBid.toBuilder()
.type(BidType.banner)
.bid(bid.toBuilder().ext(modifiedBidExt).build())
.build();
}

private boolean hasNativeAdm(String adm, String bidId, String bidder) {
final JsonNode admNode;
try {
admNode = mapper.readTree(adm);
} catch (JsonProcessingException e) {
return false;
}

final boolean hasAssets = admNode.has("assets");
final String warningMessage = hasAssets
? NATIVE_ADM_MESSAGE.formatted(bidId, bidder)
: ADM_WITH_NO_ASSETS_MESSAGE.formatted(bidId, bidder);

conditionalLogger.warn(warningMessage, logSamplingRate);
return hasAssets;
}

private static boolean isVideoWithVastXml(BidType type, String adm) {
return type == BidType.video && VAST_XML_PATTERN.matcher(adm).matches();
}

private ExtBidPrebid parseExtBidPrebid(Bid bid) {
try {
return mapper.convertValue(bid.getExt(), EXT_BID_PREBID_TYPE_REFERENCE).getPrebid();
} catch (Exception e) {
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.prebid.server.hooks.modules.pb.response.correction.core.correction.appvideohtml;

import com.iab.openrtb.request.BidRequest;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.AppVideoHtmlConfig;
import org.prebid.server.hooks.modules.pb.response.correction.core.config.model.Config;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.Correction;
import org.prebid.server.hooks.modules.pb.response.correction.core.correction.CorrectionProducer;

public class AppVideoHtmlCorrectionProducer implements CorrectionProducer {

private final AppVideoHtmlCorrection correctionInstance;

public AppVideoHtmlCorrectionProducer(AppVideoHtmlCorrection correction) {
this.correctionInstance = correction;
}

@Override
public boolean shouldProduce(Config config, BidRequest bidRequest) {
final AppVideoHtmlConfig appVideoHtmlConfig = config.getAppVideoHtmlConfig();
final boolean enabled = appVideoHtmlConfig != null && appVideoHtmlConfig.isEnabled();
return enabled && bidRequest.getApp() != null;
}

@Override
public Correction produce() {
return correctionInstance;
}
}
Loading

0 comments on commit ec4a599

Please sign in to comment.