Skip to content

Commit 55d3e57

Browse files
authored
Confiant AdQuality: Add config to exclude bidders from scanning, analytics tags (prebid#2670)
1 parent f15e7eb commit 55d3e57

25 files changed

+530
-269
lines changed

extra/modules/confiant-ad-quality/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ And configure
5252
- `long-interval-attempts` - Maximum attempts with long interval value to try to reconnect to Confiant's Redis server in case any connection error happens. This attempts are used when short-attempts were not successful.
5353
- `long-interval` - Long time interval in milliseconds after which another one attempt to connect to Redis will be executed.
5454
- `scan-state-check-interval` - Time interval in milliseconds between periodic calls to check if scan state is enabled on the side of Redis server.
55+
- `bidders-to-exclude-from-scan` - List of bidders which won't be scanned by Confiant
5556

5657
```yaml
5758
hooks:
@@ -73,6 +74,9 @@ hooks:
7374
long-interval-attempts: 336
7475
long-interval: 1800000
7576
scan-state-check-interval: 100000
77+
bidders-to-exclude-from-scan: >
78+
adyoulike,
79+
rtbhouse
7680
```
7781

7882
## Maintainer contacts

extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/config/ConfiantAdQualityModuleConfiguration.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package org.prebid.server.hooks.modules.com.confiant.adquality.config;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import io.vertx.core.Promise;
45
import io.vertx.core.Vertx;
56
import org.prebid.server.auction.PrivacyEnforcementService;
67
import org.prebid.server.hooks.modules.com.confiant.adquality.core.BidsScanner;
7-
import org.prebid.server.hooks.modules.com.confiant.adquality.core.RedisScanStateChecker;
88
import org.prebid.server.hooks.modules.com.confiant.adquality.core.RedisClient;
9+
import org.prebid.server.hooks.modules.com.confiant.adquality.core.RedisScanStateChecker;
910
import org.prebid.server.hooks.modules.com.confiant.adquality.model.RedisConfig;
1011
import org.prebid.server.hooks.modules.com.confiant.adquality.model.RedisConnectionConfig;
1112
import org.prebid.server.hooks.modules.com.confiant.adquality.model.RedisRetryConfig;
@@ -32,18 +33,21 @@ public class ConfiantAdQualityModuleConfiguration {
3233
ConfiantAdQualityModule confiantAdQualityModule(
3334
@Value("${hooks.modules.confiant-ad-quality.api-key}") String apiKey,
3435
@Value("${hooks.modules.confiant-ad-quality.scan-state-check-interval}") int scanStateCheckInterval,
36+
@Value("${hooks.modules.confiant-ad-quality.bidders-to-exclude-from-scan}") List<String> biddersToExcludeFromScan,
3537
RedisConfig redisConfig,
3638
RedisRetryConfig retryConfig,
3739
Vertx vertx,
38-
PrivacyEnforcementService privacyEnforcementService) {
40+
PrivacyEnforcementService privacyEnforcementService,
41+
ObjectMapper objectMapper) {
42+
3943
final RedisConnectionConfig writeNodeConfig = redisConfig.getWriteNode();
4044
final RedisClient writeRedisNode = new RedisClient(
4145
vertx, writeNodeConfig.getHost(), writeNodeConfig.getPort(), writeNodeConfig.getPassword(), retryConfig, "write node");
4246
final RedisConnectionConfig readNodeConfig = redisConfig.getReadNode();
4347
final RedisClient readRedisNode = new RedisClient(
4448
vertx, readNodeConfig.getHost(), readNodeConfig.getPort(), readNodeConfig.getPassword(), retryConfig, "read node");
4549

46-
final BidsScanner bidsScanner = new BidsScanner(writeRedisNode, readRedisNode, apiKey);
50+
final BidsScanner bidsScanner = new BidsScanner(writeRedisNode, readRedisNode, apiKey, objectMapper);
4751
final RedisScanStateChecker redisScanStateChecker = new RedisScanStateChecker(bidsScanner, scanStateCheckInterval, vertx);
4852

4953
final Promise<Void> scannerPromise = Promise.promise();
@@ -52,9 +56,14 @@ ConfiantAdQualityModule confiantAdQualityModule(
5256
bidsScanner.start(scannerPromise);
5357

5458
return new ConfiantAdQualityModule(List.of(
55-
new ConfiantAdQualityBidResponsesScanHook(bidsScanner, privacyEnforcementService)));
59+
new ConfiantAdQualityBidResponsesScanHook(bidsScanner, biddersToExcludeFromScan, privacyEnforcementService)));
5660
}
5761

62+
@Bean
63+
ObjectMapper objectMapper() {
64+
return new ObjectMapper();
65+
};
66+
5867
@Bean
5968
@ConfigurationProperties(prefix = "hooks.modules.confiant-ad-quality.redis-config")
6069
RedisConfig redisConfig() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.prebid.server.hooks.modules.com.confiant.adquality.core;
2+
3+
import com.iab.openrtb.response.Bid;
4+
import org.prebid.server.auction.model.BidderResponse;
5+
import org.prebid.server.bidder.model.BidderBid;
6+
import org.prebid.server.hooks.modules.com.confiant.adquality.v1.model.analytics.ActivityImpl;
7+
import org.prebid.server.hooks.modules.com.confiant.adquality.v1.model.analytics.AppliedToImpl;
8+
import org.prebid.server.hooks.modules.com.confiant.adquality.v1.model.analytics.ResultImpl;
9+
import org.prebid.server.hooks.modules.com.confiant.adquality.v1.model.analytics.TagsImpl;
10+
import org.prebid.server.hooks.v1.analytics.AppliedTo;
11+
import org.prebid.server.hooks.v1.analytics.Result;
12+
import org.prebid.server.hooks.v1.analytics.Tags;
13+
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
19+
public class AnalyticsMapper {
20+
21+
private static final String AD_QUALITY_SCAN = "ad-scan";
22+
private static final String SUCCESS_STATUS = "success";
23+
private static final String SKIPPED = "skipped";
24+
private static final String INSPECTED_HAS_ISSUE = "inspected-has-issue";
25+
private static final String INSPECTED_NO_ISSUES = "inspected-no-issues";
26+
27+
public static Tags toAnalyticsTags(List<BidderResponse> bidderResponsesWithIssues,
28+
List<BidderResponse> bidderResponsesWithoutIssues,
29+
List<BidderResponse> bidderResponsesNotScanned) {
30+
31+
return TagsImpl.of(Collections.singletonList(ActivityImpl.of(
32+
AD_QUALITY_SCAN,
33+
SUCCESS_STATUS,
34+
toActivityResults(bidderResponsesWithIssues, bidderResponsesWithoutIssues, bidderResponsesNotScanned))));
35+
}
36+
37+
private static List<Result> toActivityResults(List<BidderResponse> bidderResponsesWithIssues,
38+
List<BidderResponse> bidderResponsesWithoutIssues,
39+
List<BidderResponse> bidderResponsesNotScanned) {
40+
41+
final List<Result> results = new ArrayList<>();
42+
if (!bidderResponsesNotScanned.isEmpty()) {
43+
results.add(ResultImpl.of(SKIPPED, null, toAppliedTo(bidderResponsesNotScanned)));
44+
}
45+
if (!bidderResponsesWithIssues.isEmpty()) {
46+
results.add(ResultImpl.of(INSPECTED_HAS_ISSUE, null, toAppliedTo(bidderResponsesWithIssues)));
47+
}
48+
if (!bidderResponsesWithoutIssues.isEmpty()) {
49+
results.add(ResultImpl.of(INSPECTED_NO_ISSUES, null, toAppliedTo(bidderResponsesWithoutIssues)));
50+
}
51+
52+
return results;
53+
}
54+
55+
private static AppliedTo toAppliedTo(List<BidderResponse> bidderResponses) {
56+
final List<Bid> bids = toBids(bidderResponses);
57+
return AppliedToImpl.builder()
58+
.bidders(bidderResponses.stream().map(BidderResponse::getBidder).toList())
59+
.impIds(bids.stream().map(Bid::getImpid).toList())
60+
.bidIds(bids.stream().map(Bid::getId).toList())
61+
.build();
62+
}
63+
64+
private static List<Bid> toBids(List<BidderResponse> bidderResponses) {
65+
return bidderResponses.stream()
66+
.map(BidderResponse::getSeatBid)
67+
.flatMap(seatBid -> seatBid.getBids().stream())
68+
.map(BidderBid::getBid)
69+
.collect(Collectors.toList());
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,49 @@
11
package org.prebid.server.hooks.modules.com.confiant.adquality.core;
22

3+
import lombok.Builder;
4+
import lombok.Value;
35
import org.prebid.server.auction.model.BidderResponse;
46
import org.prebid.server.hooks.modules.com.confiant.adquality.model.BidScanResult;
5-
import org.prebid.server.hooks.modules.com.confiant.adquality.model.OperationResult;
7+
import org.prebid.server.hooks.modules.com.confiant.adquality.model.GroupByIssues;
68

9+
import java.util.ArrayList;
710
import java.util.List;
8-
import java.util.Optional;
9-
import java.util.stream.IntStream;
1011

12+
@Builder
13+
@Value(staticConstructor = "of")
1114
public class BidsScanResult {
1215

13-
private final OperationResult<List<BidScanResult>> result;
16+
List<BidScanResult> bidScanResults;
1417

15-
public BidsScanResult(OperationResult<List<BidScanResult>> result) {
16-
this.result = result;
17-
}
18+
List<String> debugMessages;
1819

19-
public boolean hasIssues() {
20-
return Optional.ofNullable(result.getValue())
21-
.map(scanResults -> scanResults.stream()
22-
.anyMatch(result -> Optional.ofNullable(result.getIssues())
23-
.map(issues -> !issues.isEmpty())
24-
.orElse(false)))
25-
.orElse(false);
26-
}
20+
public GroupByIssues<BidderResponse> toGroupByIssues(List<BidderResponse> bidderResponses) {
21+
final List<BidderResponse> bidderResponsesWithIssues = new ArrayList<>();
22+
final List<BidderResponse> bidderResponsesWithoutIssues = new ArrayList<>();
23+
final int groupSize = bidderResponses.size();
2724

28-
public List<BidderResponse> filterValidResponses(List<BidderResponse> responses) {
29-
return IntStream.range(0, responses.size())
30-
.filter(ind -> !hasIssuesByBidIndex(ind))
31-
.mapToObj(responses::get)
32-
.toList();
25+
for (int i = 0; i < groupSize; i++) {
26+
if (hasIssuesByBidIndex(i)) {
27+
bidderResponsesWithIssues.add(bidderResponses.get(i));
28+
} else {
29+
bidderResponsesWithoutIssues.add(bidderResponses.get(i));
30+
}
31+
}
32+
33+
return GroupByIssues.<BidderResponse>builder()
34+
.withIssues(bidderResponsesWithIssues)
35+
.withoutIssues(bidderResponsesWithoutIssues)
36+
.build();
3337
}
3438

3539
public List<String> getIssuesMessages() {
36-
return result.getValue().stream()
40+
return bidScanResults.stream()
3741
.map(r -> r.getTagKey() + ": " + (r.getIssues() == null ? "no issues" : r.getIssues().toString()))
3842
.toList();
3943
}
4044

41-
public List<String> getDebugMessages() {
42-
return result.getDebugMessages();
43-
}
44-
4545
private boolean hasIssuesByBidIndex(Integer ind) {
46-
final BidScanResult bidResult = result.getValue().get(ind);
47-
return bidResult != null && bidResult.getIssues() != null && bidResult.getIssues().size() > 0;
46+
final BidScanResult bidResult = bidScanResults.size() > ind ? bidScanResults.get(ind) : null;
47+
return bidResult != null && bidResult.getIssues() != null && !bidResult.getIssues().isEmpty();
4848
}
4949
}

extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77
import io.vertx.redis.client.RedisAPI;
88
import io.vertx.redis.client.Response;
99
import org.prebid.server.hooks.modules.com.confiant.adquality.model.BidScanResult;
10-
import org.prebid.server.hooks.modules.com.confiant.adquality.model.OperationResult;
1110
import org.prebid.server.hooks.modules.com.confiant.adquality.model.RedisBidsData;
1211

1312
import java.util.Collections;
1413
import java.util.List;
1514

1615
public class BidsScanner {
1716

18-
private final RedisParser redisParser = new RedisParser();
17+
private final RedisParser redisParser;
1918

2019
private final String apiKey;
2120

@@ -25,12 +24,19 @@ public class BidsScanner {
2524

2625
private volatile Boolean isScanDisabled = true;
2726

28-
private final ObjectMapper objectMapper = new ObjectMapper();
27+
private final ObjectMapper objectMapper;
28+
29+
public BidsScanner(
30+
RedisClient writeRedisNode,
31+
RedisClient readRedisNode,
32+
String apiKey,
33+
ObjectMapper objectMapper) {
2934

30-
public BidsScanner(RedisClient writeRedisNode, RedisClient readRedisNode, String apiKey) {
3135
this.writeRedisNode = writeRedisNode;
3236
this.readRedisNode = readRedisNode;
3337
this.apiKey = apiKey;
38+
this.objectMapper = objectMapper;
39+
this.redisParser = new RedisParser(objectMapper);
3440
}
3541

3642
public void start(Promise<Void> startFuture) {
@@ -63,15 +69,15 @@ public Future<BidsScanResult> submitBids(RedisBidsData bids) {
6369

6470
readRedisNodeAPI.evalsha(readArgs, response -> {
6571
if (response.result() != null) {
66-
final OperationResult<List<BidScanResult>> parserResult = redisParser
72+
final BidsScanResult parserResult = redisParser
6773
.parseBidsScanResult(response.result().toString());
68-
final boolean isAnyRoSkipped = parserResult.getValue()
74+
final boolean isAnyRoSkipped = parserResult.getBidScanResults()
6975
.stream().anyMatch(BidScanResult::isRoSkipped);
7076

7177
if (isAnyRoSkipped) {
7278
reSubmitBidsToWriteNode(readArgs, scanResult);
7379
} else {
74-
scanResult.complete(new BidsScanResult(parserResult));
80+
scanResult.complete(parserResult);
7581
}
7682
} else {
7783
scanResult.complete(getEmptyScanResult());
@@ -94,10 +100,10 @@ private void reSubmitBidsToWriteNode(List<String> readArgs, Promise<BidsScanResu
94100
final List<String> writeArgs = readArgs.stream().limit(4).toList();
95101
writeRedisAPI.evalsha(writeArgs, response -> {
96102
if (response.result() != null) {
97-
final OperationResult<List<BidScanResult>> parserResult = redisParser
103+
final BidsScanResult parserResult = redisParser
98104
.parseBidsScanResult(response.result().toString());
99105

100-
scanResult.complete(new BidsScanResult(parserResult));
106+
scanResult.complete(parserResult);
101107
} else {
102108
scanResult.complete(getEmptyScanResult());
103109
}
@@ -132,9 +138,9 @@ private String toBidsAsJson(RedisBidsData bids) {
132138
}
133139

134140
private BidsScanResult getEmptyScanResult() {
135-
return new BidsScanResult(OperationResult.<List<BidScanResult>>builder()
136-
.value(Collections.emptyList())
141+
return BidsScanResult.builder()
142+
.bidScanResults(Collections.emptyList())
137143
.debugMessages(Collections.emptyList())
138-
.build());
144+
.build();
139145
}
140146
}

extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public RedisClient(
3535
String password,
3636
RedisRetryConfig retryConfig,
3737
String type) {
38+
3839
this.vertx = vertx;
3940
this.retryConfig = retryConfig;
4041
this.options = new RedisOptions().setConnectionString("redis://:" + password + "@" + host + ":" + port);

extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisParser.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import io.vertx.core.logging.Logger;
66
import io.vertx.core.logging.LoggerFactory;
77
import org.prebid.server.hooks.modules.com.confiant.adquality.model.BidScanResult;
8-
import org.prebid.server.hooks.modules.com.confiant.adquality.model.OperationResult;
98
import org.prebid.server.hooks.modules.com.confiant.adquality.model.RedisError;
109

1110
import java.util.Arrays;
@@ -16,18 +15,22 @@ public class RedisParser {
1615

1716
private static final Logger logger = LoggerFactory.getLogger(RedisParser.class);
1817

19-
public OperationResult<List<BidScanResult>> parseBidsScanResult(String redisResponse) {
20-
final ObjectMapper objectMapper = new ObjectMapper();
18+
private final ObjectMapper objectMapper;
2119

20+
public RedisParser(ObjectMapper objectMapper) {
21+
this.objectMapper = objectMapper;
22+
}
23+
24+
public BidsScanResult parseBidsScanResult(String redisResponse) {
2225
try {
2326
final BidScanResult[][][] draftResponse = objectMapper.readValue(redisResponse, BidScanResult[][][].class);
2427
final List<BidScanResult> scanResultsFlat = Arrays.stream(draftResponse)
2528
.flatMap(Arrays::stream)
2629
.map(array -> array[0])
2730
.toList();
2831

29-
return OperationResult.<List<BidScanResult>>builder()
30-
.value(scanResultsFlat)
32+
return BidsScanResult.builder()
33+
.bidScanResults(scanResultsFlat)
3134
.debugMessages(Collections.emptyList())
3235
.build();
3336
} catch (JsonProcessingException resultParse) {
@@ -39,8 +42,8 @@ public OperationResult<List<BidScanResult>> parseBidsScanResult(String redisResp
3942
message = String.format("Error during parse redis response: %s", redisResponse);
4043
}
4144
logger.info(message);
42-
return OperationResult.<List<BidScanResult>>builder()
43-
.value(Collections.emptyList())
45+
return BidsScanResult.builder()
46+
.bidScanResults(Collections.emptyList())
4447
.debugMessages(List.of(message))
4548
.build();
4649
}

extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisScanStateChecker.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public RedisScanStateChecker(
1414
BidsScanner bidsScanner,
1515
long scanStateCheckInterval,
1616
Vertx vertx) {
17+
1718
this.bidsScanner = bidsScanner;
1819
this.scanStateCheckInterval = scanStateCheckInterval;
1920
this.vertx = vertx;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.prebid.server.hooks.modules.com.confiant.adquality.model;
2+
3+
import lombok.Builder;
4+
import lombok.Value;
5+
6+
import java.util.List;
7+
8+
@Builder(toBuilder = true)
9+
@Value
10+
public class GroupByIssues<T> {
11+
12+
List<T> withIssues;
13+
14+
List<T> withoutIssues;
15+
}

0 commit comments

Comments
 (0)