Skip to content

Commit

Permalink
Buyeruid Scrubbed Metric (#3674)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoxaAntoxic authored Jan 28, 2025
1 parent 7861a1e commit 8b6d9e9
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.prebid.server.auction.model.AuctionContext;
import org.prebid.server.auction.model.BidderPrivacyResult;
import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask;
import org.prebid.server.bidder.BidderCatalog;
import org.prebid.server.log.Logger;
import org.prebid.server.log.LoggerFactory;
import org.prebid.server.metric.MetricName;
Expand Down Expand Up @@ -38,19 +37,16 @@ public class TcfEnforcement implements PrivacyEnforcement {

private final TcfDefinerService tcfDefinerService;
private final UserFpdTcfMask userFpdTcfMask;
private final BidderCatalog bidderCatalog;
private final Metrics metrics;
private final boolean lmtEnforce;

public TcfEnforcement(TcfDefinerService tcfDefinerService,
UserFpdTcfMask userFpdTcfMask,
BidderCatalog bidderCatalog,
Metrics metrics,
boolean lmtEnforce) {

this.tcfDefinerService = Objects.requireNonNull(tcfDefinerService);
this.userFpdTcfMask = Objects.requireNonNull(userFpdTcfMask);
this.bidderCatalog = Objects.requireNonNull(bidderCatalog);
this.metrics = Objects.requireNonNull(metrics);
this.lmtEnforce = lmtEnforce;
}
Expand All @@ -67,6 +63,7 @@ public Future<List<BidderPrivacyResult>> enforce(AuctionContext auctionContext,

final MetricName requestType = auctionContext.getRequestTypeMetric();
final ActivityInfrastructure activityInfrastructure = auctionContext.getActivityInfrastructure();
final Account account = auctionContext.getAccount();
final Set<String> bidders = results.stream()
.map(BidderPrivacyResult::getRequestBidder)
.collect(Collectors.toSet());
Expand All @@ -75,9 +72,15 @@ public Future<List<BidderPrivacyResult>> enforce(AuctionContext auctionContext,
bidders,
VendorIdResolver.of(aliases),
auctionContext.getPrivacyContext().getTcfContext(),
accountGdprConfig(auctionContext.getAccount()))
accountGdprConfig(account))
.map(TcfResponse::getActions)
.map(enforcements -> updateMetrics(activityInfrastructure, enforcements, aliases, requestType, results))
.map(enforcements -> updateMetrics(
activityInfrastructure,
enforcements,
aliases,
requestType,
results,
account))
.map(enforcements -> applyEnforcements(enforcements, results));
}

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

private static boolean hasBuyerUid(User user) {
return user != null && user.getBuyeruid() != null;
}

private Map<String, PrivacyEnforcementAction> updateMetrics(ActivityInfrastructure activityInfrastructure,
Map<String, PrivacyEnforcementAction> enforcements,
BidderAliases aliases,
MetricName requestType,
List<BidderPrivacyResult> results) {
List<BidderPrivacyResult> results,
Account account) {

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

if (hasBuyerUid(user) && ufpdRemoved) {
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(bidder, account);
}

if (ufpdRemoved) {
logger.warn("The UFPD fields have been removed due to a consent check.");
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/prebid/server/metric/MetricName.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public enum MetricName {
unknown_error,
err,
networkerr,
buyeruid_scrubbed,

// bids validation
warn,
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/prebid/server/metric/Metrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,13 @@ public void updateAdapterRequestTypeAndNoCookieMetrics(String bidder, MetricName
}
}

public void updateAdapterRequestBuyerUidScrubbedMetrics(String bidder, Account account) {
forAdapter(bidder).request().incCounter(MetricName.buyeruid_scrubbed);
if (accountMetricsVerbosityResolver.forAccount(account).isAtLeast(AccountMetricsVerbosityLevel.detailed)) {
forAccount(account.getId()).adapter().forAdapter(bidder).request().incCounter(MetricName.buyeruid_scrubbed);
}
}

public void updateAdapterResponseTime(String bidder, Account account, int responseTime) {
final AdapterTypeMetrics adapterTypeMetrics = forAdapter(bidder);
adapterTypeMetrics.updateTimer(MetricName.request_time, responseTime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,10 @@ CoppaEnforcement coppaEnforcement(UserFpdCoppaMask userFpdCoppaMask, Metrics met
@Bean
TcfEnforcement tcfEnforcement(TcfDefinerService tcfDefinerService,
UserFpdTcfMask userFpdTcfMask,
BidderCatalog bidderCatalog,
Metrics metrics,
@Value("${lmt.enforce}") boolean lmtEnforce) {

return new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, lmtEnforce);
return new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, lmtEnforce);
}

@Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.prebid.server.functional.tests.privacy
import org.mockserver.model.Delay
import org.prebid.server.functional.model.ChannelType
import org.prebid.server.functional.model.config.AccountGdprConfig
import org.prebid.server.functional.model.config.AccountMetricsConfig
import org.prebid.server.functional.model.config.AccountMetricsVerbosityLevel
import org.prebid.server.functional.model.config.PurposeConfig
import org.prebid.server.functional.model.config.PurposeEnforcement
import org.prebid.server.functional.model.pricefloors.Country
Expand All @@ -21,6 +23,8 @@ import java.time.Instant
import static org.prebid.server.functional.model.ChannelType.PBJS
import static org.prebid.server.functional.model.ChannelType.WEB
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC

import static org.prebid.server.functional.model.config.AccountMetricsVerbosityLevel.DETAILED
import static org.prebid.server.functional.model.config.Purpose.P1
import static org.prebid.server.functional.model.config.Purpose.P2
import static org.prebid.server.functional.model.config.Purpose.P4
Expand Down Expand Up @@ -960,4 +964,139 @@ class GdprAuctionSpec extends PrivacyBaseSpec {
null | BULGARIA | null | null | [:]
null | null | null | null | [:]
}

def "PBS auction shouldn't update buyeruid scrubbed metrics when user.buyeruid not requested"() {
given: "Default bid requests with personal data"
def bidRequest = bidRequestWithPersonalData.tap {
regs.gdpr = 1
user.buyeruid = null
user.ext.consent = new TcfConsent.Builder().build()
ext.prebid.trace = VERBOSE
}

and: "Save account config with requireConsent into DB"
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
config.metrics = new AccountMetricsConfig(verbosityLevel: verbosityLevel)
}
accountDao.save(account)

and: "Flush metric"
flushMetrics(privacyPbsService)

when: "PBS processes auction requests"
privacyPbsService.sendAuctionRequest(bidRequest)

then: "Bidder request should mask user personal data"
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
verifyAll(bidderRequest.user) {
!id
!buyeruid
!yob
!gender
!eids
!data
!geo
!ext
!eids
!ext?.eids
}

and: "Metrics buyeruid scrubbed shouldn't be updated"
def metrics = privacyPbsService.sendCollectedMetricsRequest()
assert !metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]
assert !metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]

where:
verbosityLevel << [DETAILED, AccountMetricsVerbosityLevel.BASIC]
}

def "PBS auction should update buyeruid scrubbed general metrics when user.buyeruid requested and verbosityLevel BASIC"() {
given: "Default bid requests with personal data"
def bidRequest = bidRequestWithPersonalData.tap {
regs.gdpr = 1
user.ext.consent = new TcfConsent.Builder().build()
ext.prebid.trace = VERBOSE
}

and: "Save account config with requireConsent into DB"
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
config.metrics = new AccountMetricsConfig(verbosityLevel: AccountMetricsVerbosityLevel.BASIC)
}
accountDao.save(account)

and: "Flush metric"
flushMetrics(privacyPbsService)

when: "PBS processes auction requests"
privacyPbsService.sendAuctionRequest(bidRequest)

then: "Bidder request should mask user personal data"
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
verifyAll(bidderRequest.user) {
!id
!buyeruid
!yob
!gender
!eids
!data
!geo
!ext
!eids
!ext?.eids
}

and: "Metrics buyeruid scrubbed should be updated"
def metrics = privacyPbsService.sendCollectedMetricsRequest()
assert metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1

and: "Account metric shouldn't be populated"
assert !metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"]
}

def "PBS auction should update buyeruid scrubbed general and account metrics when user.buyeruid requested and verbosityLevel DETAILED"() {
given: "Default bid requests with personal data"
def bidRequest = bidRequestWithPersonalData.tap {
regs.gdpr = 1
user.ext.consent = new TcfConsent.Builder().build()
ext.prebid.trace = VERBOSE
}

and: "Save account config with requireConsent into DB"
def purposes = [(P2): new PurposeConfig(enforcePurpose: NO, enforceVendors: false)]
def accountGdprConfig = new AccountGdprConfig(purposes: purposes)
def account = getAccountWithGdpr(bidRequest.accountId, accountGdprConfig).tap {
config.metrics = new AccountMetricsConfig(verbosityLevel: DETAILED)
}
accountDao.save(account)

and: "Flush metric"
flushMetrics(privacyPbsService)

when: "PBS processes auction requests"
privacyPbsService.sendAuctionRequest(bidRequest)

then: "Bidder request should mask user personal data"
def bidderRequest = bidder.getBidderRequest(bidRequest.id)
verifyAll(bidderRequest.user) {
!id
!buyeruid
!yob
!gender
!eids
!data
!geo
!ext
!eids
!ext?.eids
}

and: "Metrics buyeruid scrubbed should be updated"
def metrics = privacyPbsService.sendCollectedMetricsRequest()
assert metrics["adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1
assert metrics["account.${account.uuid}.adapter.${GENERIC.value}.requests.buyeruid_scrubbed"] == 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.prebid.server.auction.model.AuctionContext;
import org.prebid.server.auction.model.BidderPrivacyResult;
import org.prebid.server.auction.privacy.enforcement.mask.UserFpdTcfMask;
import org.prebid.server.bidder.BidderCatalog;
import org.prebid.server.metric.MetricName;
import org.prebid.server.metric.Metrics;
import org.prebid.server.privacy.gdpr.TcfDefinerService;
Expand Down Expand Up @@ -44,6 +43,7 @@
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mock.Strictness.LENIENT;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

@ExtendWith(MockitoExtension.class)
Expand All @@ -54,8 +54,6 @@ public class TcfEnforcementTest {
@Mock(strictness = LENIENT)
private UserFpdTcfMask userFpdTcfMask;
@Mock
private BidderCatalog bidderCatalog;
@Mock
private Metrics metrics;

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

target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, true);
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, true);

given(aliases.resolveBidder(anyString()))
.willAnswer(invocation -> invocation.getArgument(0));
Expand Down Expand Up @@ -169,6 +167,44 @@ public void enforceShouldEmitExpectedMetricsWhenUserHasPrivacyData() {
verifyMetric("bidder2", true, false, false, false, false, false);
}

@Test
public void enforceShouldEmitBuyerUidScrubbedMetricsWhenUserHasBuyerUid() {
// give
givenPrivacyEnforcementActions(Map.of(
"bidder0", givenEnforcementAction(PrivacyEnforcementAction::setMaskGeo),
"bidder1", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo),
"bidder2", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd),
"bidder3", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo),
"bidder4", givenEnforcementAction(PrivacyEnforcementAction::setRemoveUserFpd),
"bidder5", givenEnforcementAction(PrivacyEnforcementAction::setMaskDeviceInfo)));

final User givenUserWithoutBuyerUid = givenUserWithPrivacyData();

final User givenUserWithBuyerUid = givenUserWithoutBuyerUid.toBuilder()
.buyeruid("buyeruid")
.build();

final AuctionContext auctionContext = givenAuctionContext(givenDeviceWithNoPrivacyData());
final List<BidderPrivacyResult> initialResults = List.of(
givenBidderPrivacyResult("bidder0", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
givenBidderPrivacyResult("bidder1", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
givenBidderPrivacyResult("bidder2", givenUserWithoutBuyerUid, givenDeviceWithNoPrivacyData()),
givenBidderPrivacyResult("bidder3", givenUserWithoutBuyerUid, givenDeviceWithPrivacyData()),
givenBidderPrivacyResult("bidder4", givenUserWithBuyerUid, givenDeviceWithNoPrivacyData()),
givenBidderPrivacyResult("bidder5", givenUserWithBuyerUid, givenDeviceWithPrivacyData()));

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

// then
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder0"), any());
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder1"), any());
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder2"), any());
verify(metrics, never()).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder3"), any());
verify(metrics).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder4"), any());
verify(metrics).updateAdapterRequestBuyerUidScrubbedMetrics(eq("bidder5"), any());
}

@Test
public void enforceShouldEmitExpectedMetricsWhenDeviceHavePrivacyData() {
// give
Expand Down Expand Up @@ -265,7 +301,7 @@ public void enforceShouldNotEmitPrivacyLmtMetricWhenLmtNotEnforced() {
final List<BidderPrivacyResult> initialResults = List.of(
givenBidderPrivacyResult("bidder", givenUserWithPrivacyData(), device));

target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, bidderCatalog, metrics, false);
target = new TcfEnforcement(tcfDefinerService, userFpdTcfMask, metrics, false);

// when
target.enforce(auctionContext, aliases, initialResults);
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/org/prebid/server/metric/MetricsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,28 @@ public void updateAdapterResponseTimeShouldUpdateMetrics() {
assertThat(metricRegistry.timer("account.accountId.adapter.conversant.request_time").getCount()).isEqualTo(2);
}

@Test
public void updateAdapterRequestBuyerUidScrubbedMetricsShouldIncrementMetrics() {
// when
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(RUBICON, Account.empty(ACCOUNT_ID));
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(CONVERSANT, Account.empty(ACCOUNT_ID));
metrics.updateAdapterRequestBuyerUidScrubbedMetrics(CONVERSANT, Account.empty(ACCOUNT_ID));

// then
assertThat(metricRegistry.counter("adapter.rubicon.requests.buyeruid_scrubbed")
.getCount())
.isOne();
assertThat(metricRegistry.counter("account.accountId.adapter.rubicon.requests.buyeruid_scrubbed")
.getCount())
.isOne();
assertThat(metricRegistry.counter("adapter.conversant.requests.buyeruid_scrubbed")
.getCount())
.isEqualTo(2);
assertThat(metricRegistry.counter("account.accountId.adapter.conversant.requests.buyeruid_scrubbed")
.getCount())
.isEqualTo(2);
}

@Test
public void updateAdapterRequestNobidMetricsShouldIncrementMetrics() {
// when
Expand Down

0 comments on commit 8b6d9e9

Please sign in to comment.