Skip to content

Commit 8b6d9e9

Browse files
Buyeruid Scrubbed Metric (#3674)
1 parent 7861a1e commit 8b6d9e9

File tree

7 files changed

+230
-14
lines changed

7 files changed

+230
-14
lines changed

src/main/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcement.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import org.prebid.server.auction.model.AuctionContext;
1111
import org.prebid.server.auction.model.BidderPrivacyResult;
1212
import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask;
13-
import org.prebid.server.bidder.BidderCatalog;
1413
import org.prebid.server.log.Logger;
1514
import org.prebid.server.log.LoggerFactory;
1615
import org.prebid.server.metric.MetricName;
@@ -38,19 +37,16 @@ public class TcfEnforcement implements PrivacyEnforcement {
3837

3938
private final TcfDefinerService tcfDefinerService;
4039
private final UserFpdTcfMask userFpdTcfMask;
41-
private final BidderCatalog bidderCatalog;
4240
private final Metrics metrics;
4341
private final boolean lmtEnforce;
4442

4543
public TcfEnforcement(TcfDefinerService tcfDefinerService,
4644
UserFpdTcfMask userFpdTcfMask,
47-
BidderCatalog bidderCatalog,
4845
Metrics metrics,
4946
boolean lmtEnforce) {
5047

5148
this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService);
5249
this.userFpdTcfMask = Objects.requireNonNull(userFpdTcfMask);
53-
this.bidderCatalog = Objects.requireNonNull(bidderCatalog);
5450
this.metrics = Objects.requireNonNull(metrics);
5551
this.lmtEnforce = lmtEnforce;
5652
}
@@ -67,6 +63,7 @@ public Future<List<BidderPrivacyResult>> enforce(AuctionContext auctionContext,
6763

6864
final MetricName requestType = auctionContext.getRequestTypeMetric();
6965
final ActivityInfrastructure activityInfrastructure = auctionContext.getActivityInfrastructure();
66+
final Account account = auctionContext.getAccount();
7067
final Set<String> bidders = results.stream()
7168
.map(BidderPrivacyResult::getRequestBidder)
7269
.collect(Collectors.toSet());
@@ -75,9 +72,15 @@ public Future<List<BidderPrivacyResult>> enforce(AuctionContext auctionContext,
7572
bidders,
7673
VendorIdResolver.of(aliases),
7774
auctionContext.getPrivacyContext().getTcfContext(),
78-
accountGdprConfig(auctionContext.getAccount()))
75+
accountGdprConfig(account))
7976
.map(TcfResponse::getActions)
80-
.map(enforcements -> updateMetrics(activityInfrastructure, enforcements, aliases, requestType, results))
77+
.map(enforcements -> updateMetrics(
78+
activityInfrastructure,
79+
enforcements,
80+
aliases,
81+
requestType,
82+
results,
83+
account))
8184
.map(enforcements -> applyEnforcements(enforcements, results));
8285
}
8386

@@ -86,11 +89,16 @@ private static AccountGdprConfig accountGdprConfig(Account account) {
8689
return privacyConfig != null ? privacyConfig.getGdpr() : null;
8790
}
8891

92+
private static boolean hasBuyerUid(User user) {
93+
return user != null && user.getBuyeruid() != null;
94+
}
95+
8996
private Map<String, PrivacyEnforcementAction> updateMetrics(ActivityInfrastructure activityInfrastructure,
9097
Map<String, PrivacyEnforcementAction> enforcements,
9198
BidderAliases aliases,
9299
MetricName requestType,
93-
List<BidderPrivacyResult> results) {
100+
List<BidderPrivacyResult> results,
101+
Account account) {
94102

95103
// Metrics should represent real picture of the bidding process, so if bidder request is blocked
96104
// by privacy then no reason to increment another metrics, like geo masked, etc.
@@ -120,6 +128,10 @@ private Map<String, PrivacyEnforcementAction> updateMetrics(ActivityInfrastructu
120128
requestBlocked,
121129
isLmtEnforcedAndEnabled);
122130

131+
if (hasBuyerUid(user) && ufpdRemoved) {
132+
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(bidder, account);
133+
}
134+
123135
if (ufpdRemoved) {
124136
logger.warn("The UFPD fields have been removed due to a consent check.");
125137
}

src/main/java/org/prebid/server/metric/MetricName.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public enum MetricName {
7272
unknown_error,
7373
err,
7474
networkerr,
75+
buyeruid_scrubbed,
7576

7677
// bids validation
7778
warn,

src/main/java/org/prebid/server/metric/Metrics.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ public void updateAdapterRequestTypeAndNoCookieMetrics(String bidder, MetricName
285285
}
286286
}
287287

288+
public void updateAdapterRequestBuyerUidScrubbedMetrics(String bidder, Account account) {
289+
forAdapter(bidder).request().incCounter(MetricName.buyeruid_scrubbed);
290+
if (accountMetricsVerbosityResolver.forAccount(account).isAtLeast(AccountMetricsVerbosityLevel.detailed)) {
291+
forAccount(account.getId()).adapter().forAdapter(bidder).request().incCounter(MetricName.buyeruid_scrubbed);
292+
}
293+
}
294+
288295
public void updateAdapterResponseTime(String bidder, Account account, int responseTime) {
289296
final AdapterTypeMetrics adapterTypeMetrics = forAdapter(bidder);
290297
adapterTypeMetrics.updateTimer(MetricName.request_time, responseTime);

src/main/java/org/prebid/server/spring/config/PrivacyServiceConfiguration.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,10 @@ CoppaEnforcement coppaEnforcement(UserFpdCoppaMask userFpdCoppaMask, Metrics met
393393
@Bean
394394
TcfEnforcement tcfEnforcement(TcfDefinerService tcfDefinerService,
395395
UserFpdTcfMask userFpdTcfMask,
396-
BidderCatalog bidderCatalog,
397396
Metrics metrics,
398397
@Value("${lmt.enforce}") boolean lmtEnforce) {
399398

400-
return new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, lmtEnforce);
399+
return new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, lmtEnforce);
401400
}
402401

403402
@Data

src/test/groovy/org/prebid/server/functional/tests/privacy/GdprAuctionSpec.groovy

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package org.prebid.server.functional.tests.privacy
33
import org.mockserver.model.Delay
44
import org.prebid.server.functional.model.ChannelType
55
import org.prebid.server.functional.model.config.AccountGdprConfig
6+
import org.prebid.server.functional.model.config.AccountMetricsConfig
7+
import org.prebid.server.functional.model.config.AccountMetricsVerbosityLevel
68
import org.prebid.server.functional.model.config.PurposeConfig
79
import org.prebid.server.functional.model.config.PurposeEnforcement
810
import org.prebid.server.functional.model.pricefloors.Country
@@ -21,6 +23,8 @@ import java.time.Instant
2123
import static org.prebid.server.functional.model.ChannelType.PBJS
2224
import static org.prebid.server.functional.model.ChannelType.WEB
2325
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC
26+
27+
import static org.prebid.server.functional.model.config.AccountMetricsVerbosityLevel.DETAILED
2428
import static org.prebid.server.functional.model.config.Purpose.P1
2529
import static org.prebid.server.functional.model.config.Purpose.P2
2630
import static org.prebid.server.functional.model.config.Purpose.P4
@@ -960,4 +964,139 @@ class GdprAuctionSpec extends PrivacyBaseSpec {
960964
null | BULGARIA | null | null | [:]
961965
null | null | null | null | [:]
962966
}
967+
968+
def "PBS auction shouldn't update buyeruid scrubbed metrics when user.buyeruid not requested"() {
969+
given: "Default bid requests with personal data"
970+
def bidRequest = bidRequestWithPersonalData.tap {
971+
regs.gdpr = 1
972+
user.buyeruid = null
973+
user.ext.consent = new TcfConsent.Builder().build()
974+
ext.prebid.trace = VERBOSE
975+
}
976+
977+
and: "Save account config with requireConsent into DB"
978+
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
979+
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
980+
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
981+
config.metrics = new AccountMetricsConfig(verbosityLevel: verbosityLevel)
982+
}
983+
accountDao.save(account)
984+
985+
and: "Flush metric"
986+
flushMetrics(privacyPbsService)
987+
988+
when: "PBS processes auction requests"
989+
privacyPbsService.sendAuctionRequest(bidRequest)
990+
991+
then: "Bidder request should mask user personal data"
992+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
993+
verifyAll(bidderRequest.user) {
994+
!id
995+
!buyeruid
996+
!yob
997+
!gender
998+
!eids
999+
!data
1000+
!geo
1001+
!ext
1002+
!eids
1003+
!ext?.eids
1004+
}
1005+
1006+
and: "Metrics buyeruid scrubbed shouldn't be updated"
1007+
def metrics = privacyPbsService.sendCollectedMetricsRequest()
1008+
assert !metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]
1009+
assert !metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]
1010+
1011+
where:
1012+
verbosityLevel << [DETAILED, AccountMetricsVerbosityLevel.BASIC]
1013+
}
1014+
1015+
def "PBS auction should update buyeruid scrubbed general metrics when user.buyeruid requested and verbosityLevel BASIC"() {
1016+
given: "Default bid requests with personal data"
1017+
def bidRequest = bidRequestWithPersonalData.tap {
1018+
regs.gdpr = 1
1019+
user.ext.consent = new TcfConsent.Builder().build()
1020+
ext.prebid.trace = VERBOSE
1021+
}
1022+
1023+
and: "Save account config with requireConsent into DB"
1024+
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
1025+
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
1026+
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
1027+
config.metrics = new AccountMetricsConfig(verbosityLevel: AccountMetricsVerbosityLevel.BASIC)
1028+
}
1029+
accountDao.save(account)
1030+
1031+
and: "Flush metric"
1032+
flushMetrics(privacyPbsService)
1033+
1034+
when: "PBS processes auction requests"
1035+
privacyPbsService.sendAuctionRequest(bidRequest)
1036+
1037+
then: "Bidder request should mask user personal data"
1038+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1039+
verifyAll(bidderRequest.user) {
1040+
!id
1041+
!buyeruid
1042+
!yob
1043+
!gender
1044+
!eids
1045+
!data
1046+
!geo
1047+
!ext
1048+
!eids
1049+
!ext?.eids
1050+
}
1051+
1052+
and: "Metrics buyeruid scrubbed should be updated"
1053+
def metrics = privacyPbsService.sendCollectedMetricsRequest()
1054+
assert metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1
1055+
1056+
and: "Account metric shouldn't be populated"
1057+
assert !metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]
1058+
}
1059+
1060+
def "PBS auction should update buyeruid scrubbed general and account metrics when user.buyeruid requested and verbosityLevel DETAILED"() {
1061+
given: "Default bid requests with personal data"
1062+
def bidRequest = bidRequestWithPersonalData.tap {
1063+
regs.gdpr = 1
1064+
user.ext.consent = new TcfConsent.Builder().build()
1065+
ext.prebid.trace = VERBOSE
1066+
}
1067+
1068+
and: "Save account config with requireConsent into DB"
1069+
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
1070+
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
1071+
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
1072+
config.metrics = new AccountMetricsConfig(verbosityLevel: DETAILED)
1073+
}
1074+
accountDao.save(account)
1075+
1076+
and: "Flush metric"
1077+
flushMetrics(privacyPbsService)
1078+
1079+
when: "PBS processes auction requests"
1080+
privacyPbsService.sendAuctionRequest(bidRequest)
1081+
1082+
then: "Bidder request should mask user personal data"
1083+
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
1084+
verifyAll(bidderRequest.user) {
1085+
!id
1086+
!buyeruid
1087+
!yob
1088+
!gender
1089+
!eids
1090+
!data
1091+
!geo
1092+
!ext
1093+
!eids
1094+
!ext?.eids
1095+
}
1096+
1097+
and: "Metrics buyeruid scrubbed should be updated"
1098+
def metrics = privacyPbsService.sendCollectedMetricsRequest()
1099+
assert metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1
1100+
assert metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1
1101+
}
9631102
}

src/test/java/org/prebid/server/auction/privacy/enforcement/TcfEnforcementTest.java

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.prebid.server.auction.model.AuctionContext;
1717
import org.prebid.server.auction.model.BidderPrivacyResult;
1818
import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask;
19-
import org.prebid.server.bidder.BidderCatalog;
2019
import org.prebid.server.metric.MetricName;
2120
import org.prebid.server.metric.Metrics;
2221
import org.prebid.server.privacy.gdpr.TcfDefinerService;
@@ -44,6 +43,7 @@
4443
import static org.mockito.ArgumentMatchers.same;
4544
import static org.mockito.BDDMockito.given;
4645
import static org.mockito.Mock.Strictness.LENIENT;
46+
import static org.mockito.Mockito.never;
4747
import static org.mockito.Mockito.verify;
4848

4949
@ExtendWith(MockitoExtension.class)
@@ -54,8 +54,6 @@ public class TcfEnforcementTest {
5454
@Mock(strictness = LENIENT)
5555
private UserFpdTcfMask userFpdTcfMask;
5656
@Mock
57-
private BidderCatalog bidderCatalog;
58-
@Mock
5957
private Metrics metrics;
6058

6159
private TcfEnforcement target;
@@ -73,7 +71,7 @@ public void setUp() {
7371
given(userFpdTcfMask.maskDevice(any(), anyBoolean(), anyBoolean(), anyBoolean()))
7472
.willAnswer(invocation -> invocation.getArgument(0));
7573

76-
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, true);
74+
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, true);
7775

7876
given(aliases.resolveBidder(anyString()))
7977
.willAnswer(invocation -> invocation.getArgument(0));
@@ -169,6 +167,44 @@ public void enforceShouldEmitExpectedMetricsWhenUserHasPrivacyData() {
169167
verifyMetric("bidder2", true, false, false, false, false, false);
170168
}
171169

170+
@Test
171+
public void enforceShouldEmitBuyerUidScrubbedMetricsWhenUserHasBuyerUid() {
172+
// give
173+
givenPrivacyEnforcementActions(Map.of(
174+
"bidder0", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo),
175+
"bidder1", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo),
176+
"bidder2", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd),
177+
"bidder3", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo),
178+
"bidder4", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd),
179+
"bidder5", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo)));
180+
181+
final User givenUserWithoutBuyerUid = givenUserWithPrivacyData();
182+
183+
final User givenUserWithBuyerUid = givenUserWithoutBuyerUid.toBuilder()
184+
.buyeruid("buyeruid")
185+
.build();
186+
187+
final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData());
188+
final List<BidderPrivacyResult> initialResults = List.of(
189+
givenBidderPrivacyResult("bidder0", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
190+
givenBidderPrivacyResult("bidder1", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
191+
givenBidderPrivacyResult("bidder2", givenUserWithoutBuyerUid, givenDeviceWithNoPrivacyData()),
192+
givenBidderPrivacyResult("bidder3", givenUserWithoutBuyerUid, givenDeviceWithPrivacyData()),
193+
givenBidderPrivacyResult("bidder4", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
194+
givenBidderPrivacyResult("bidder5", givenUserWithBuyerUid, givenDeviceWithPrivacyData()));
195+
196+
// when
197+
target.enforce(auctionContext, aliases, initialResults);
198+
199+
// then
200+
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder0"), any());
201+
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder1"), any());
202+
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder2"), any());
203+
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder3"), any());
204+
verify(metrics).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder4"), any());
205+
verify(metrics).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder5"), any());
206+
}
207+
172208
@Test
173209
public void enforceShouldEmitExpectedMetricsWhenDeviceHavePrivacyData() {
174210
// give
@@ -265,7 +301,7 @@ public void enforceShouldNotEmitPrivacyLmtMetricWhenLmtNotEnforced() {
265301
final List<BidderPrivacyResult> initialResults = List.of(
266302
givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device));
267303

268-
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, false);
304+
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, false);
269305

270306
// when
271307
target.enforce(auctionContext, aliases, initialResults);

src/test/java/org/prebid/server/metric/MetricsTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,28 @@ public void updateAdapterResponseTimeShouldUpdateMetrics() {
538538
assertThat(metricRegistry.timer("account.accountId.adapter.conversant.request_time").getCount()).isEqualTo(2);
539539
}
540540

541+
@Test
542+
public void updateAdapterRequestBuyerUidScrubbedMetricsShouldIncrementMetrics() {
543+
// when
544+
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(RUBICON, Account.empty(ACCOUNT_ID));
545+
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(CONVERSANT, Account.empty(ACCOUNT_ID));
546+
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(CONVERSANT, Account.empty(ACCOUNT_ID));
547+
548+
// then
549+
assertThat(metricRegistry.counter("adapter.rubicon.requests.buyeruid_scrubbed")
550+
.getCount())
551+
.isOne();
552+
assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.requests.buyeruid_scrubbed")
553+
.getCount())
554+
.isOne();
555+
assertThat(metricRegistry.counter("adapter.conversant.requests.buyeruid_scrubbed")
556+
.getCount())
557+
.isEqualTo(2);
558+
assertThat(metricRegistry.counter("account.accountId.adapter.conversant.requests.buyeruid_scrubbed")
559+
.getCount())
560+
.isEqualTo(2);
561+
}
562+
541563
@Test
542564
public void updateAdapterRequestNobidMetricsShouldIncrementMetrics() {
543565
// when

0 commit comments

Comments
 (0)