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

Refactor Default Account Resolving #3012

Merged
merged 10 commits into from
Mar 26, 2024
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.activity.utils;
package org.prebid.server.activity;

import org.prebid.server.activity.Activity;
import io.vertx.core.logging.LoggerFactory;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.settings.model.Account;
import org.prebid.server.settings.model.AccountPrivacyConfig;
import org.prebid.server.settings.model.activity.AccountActivityConfiguration;
Expand All @@ -14,27 +15,51 @@
import java.util.Optional;
import java.util.stream.Collectors;

public class AccountActivitiesConfigurationUtils {
public class ActivitiesConfigResolver {

private AccountActivitiesConfigurationUtils() {
private static final ConditionalLogger conditionalLogger =
new ConditionalLogger(LoggerFactory.getLogger(ActivitiesConfigResolver.class));

private final double logSamplingRate;

public ActivitiesConfigResolver(double logSamplingRate) {
this.logSamplingRate = logSamplingRate;
}

public Account resolve(Account account) {
if (!isInvalidActivitiesConfiguration(account)) {
return account;
}

conditionalLogger.warn(
"Activity configuration for account %s contains conditional rule with empty array."
.formatted(account.getId()),
logSamplingRate);

final AccountPrivacyConfig accountPrivacyConfig = account.getPrivacy();
return account.toBuilder()
.privacy(accountPrivacyConfig.toBuilder()
.activities(removeInvalidRules(accountPrivacyConfig.getActivities()))
.build())
.build();
}

public static boolean isInvalidActivitiesConfiguration(Account account) {
private static boolean isInvalidActivitiesConfiguration(Account account) {
return Optional.ofNullable(account)
.map(Account::getPrivacy)
.map(AccountPrivacyConfig::getActivities)
.stream()
.map(Map::values)
.flatMap(Collection::stream)
.anyMatch(AccountActivitiesConfigurationUtils::containsInvalidRule);
.anyMatch(ActivitiesConfigResolver::containsInvalidRule);
}

private static boolean containsInvalidRule(AccountActivityConfiguration accountActivityConfiguration) {
return Optional.ofNullable(accountActivityConfiguration)
.map(AccountActivityConfiguration::getRules)
.stream()
.flatMap(Collection::stream)
.anyMatch(AccountActivitiesConfigurationUtils::isInvalidConditionRule);
.anyMatch(ActivitiesConfigResolver::isInvalidConditionRule);
}

private static boolean isInvalidConditionRule(AccountActivityRuleConfig rule) {
Expand Down Expand Up @@ -63,13 +88,13 @@ private static <E> boolean isEmptyNotNull(Collection<E> collection) {
return collection != null && collection.isEmpty();
}

public static Map<Activity, AccountActivityConfiguration> removeInvalidRules(
private static Map<Activity, AccountActivityConfiguration> removeInvalidRules(
Map<Activity, AccountActivityConfiguration> activitiesConfiguration) {

return activitiesConfiguration.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> AccountActivitiesConfigurationUtils.removeInvalidRules(entry.getValue())));
entry -> removeInvalidRules(entry.getValue())));
}

private static AccountActivityConfiguration removeInvalidRules(AccountActivityConfiguration activityConfiguration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ public class Ortb2RequestFactory {
private static final ConditionalLogger EMPTY_ACCOUNT_LOGGER = new ConditionalLogger("empty_account", logger);
private static final ConditionalLogger UNKNOWN_ACCOUNT_LOGGER = new ConditionalLogger("unknown_account", logger);

private final boolean enforceValidAccount;
private final int timeoutAdjustmentFactor;
private final double logSamplingRate;
private final List<String> blacklistedAccounts;
Expand All @@ -110,8 +109,7 @@ public class Ortb2RequestFactory {
private final Metrics metrics;
private final Clock clock;

public Ortb2RequestFactory(boolean enforceValidAccount,
int timeoutAdjustmentFactor,
public Ortb2RequestFactory(int timeoutAdjustmentFactor,
double logSamplingRate,
List<String> blacklistedAccounts,
UidsCookieService uidsCookieService,
Expand All @@ -133,7 +131,6 @@ public Ortb2RequestFactory(boolean enforceValidAccount,
throw new IllegalArgumentException("Expected timeout adjustment factor should be in [0, 100].");
}

this.enforceValidAccount = enforceValidAccount;
this.timeoutAdjustmentFactor = timeoutAdjustmentFactor;
this.logSamplingRate = logSamplingRate;
this.blacklistedAccounts = Objects.requireNonNull(blacklistedAccounts);
Expand Down Expand Up @@ -426,20 +423,26 @@ private String validateIfAccountBlacklisted(String accountId) {
return accountId;
}

private Future<Account> loadAccount(Timeout timeout,
HttpRequestContext httpRequest,
String accountId) {

final Future<Account> accountFuture = StringUtils.isBlank(accountId)
? responseForEmptyAccount(httpRequest)
: applicationSettings.getAccountById(accountId, timeout)
.compose(this::ensureAccountActive,
exception -> accountFallback(exception, accountId, httpRequest));
private Future<Account> loadAccount(Timeout timeout, HttpRequestContext httpRequest, String accountId) {
if (StringUtils.isBlank(accountId)) {
EMPTY_ACCOUNT_LOGGER.warn(accountErrorMessage("Account not specified", httpRequest), logSamplingRate);
}

return accountFuture
return applicationSettings.getAccountById(accountId, timeout)
.compose(this::ensureAccountActive)
.recover(exception -> wrapFailure(exception, accountId, httpRequest))
.onFailure(ignored -> metrics.updateAccountRequestRejectedByInvalidAccountMetrics(accountId));
}

private Future<Account> ensureAccountActive(Account account) {
final String accountId = account.getId();

return account.getStatus() == AccountStatus.inactive
? Future.failedFuture(
new UnauthorizedAccountException("Account %s is inactive".formatted(accountId), accountId))
: Future.succeededFuture(account);
}

/**
* Extracts publisher id either from {@link BidRequest}.app.publisher or {@link BidRequest}.site.publisher.
* If neither is present returns empty string.
Expand Down Expand Up @@ -474,48 +477,26 @@ private String parentAccountIdFromExtPublisher(ExtPublisher extPublisher) {
return extPublisherPrebid != null ? StringUtils.stripToNull(extPublisherPrebid.getParentAccount()) : null;
}

private Future<Account> responseForEmptyAccount(HttpRequestContext httpRequest) {
EMPTY_ACCOUNT_LOGGER.warn(accountErrorMessage("Account not specified", httpRequest), logSamplingRate);
return responseForUnknownAccount(StringUtils.EMPTY);
}

private static String accountErrorMessage(String message, HttpRequestContext httpRequest) {
return "%s, Url: %s and Referer: %s".formatted(
message,
httpRequest.getAbsoluteUri(),
httpRequest.getHeaders().get(HttpUtil.REFERER_HEADER));
}

private Future<Account> accountFallback(Throwable exception,
String accountId,
HttpRequestContext httpRequest) {

if (exception instanceof PreBidException) {
private Future<Account> wrapFailure(Throwable exception, String accountId, HttpRequestContext httpRequest) {
if (exception instanceof UnauthorizedAccountException) {
return Future.failedFuture(exception);
} else if (exception instanceof PreBidException) {
UNKNOWN_ACCOUNT_LOGGER.warn(accountErrorMessage(exception.getMessage(), httpRequest), 100);
} else {
metrics.updateAccountRequestRejectedByFailedFetch(accountId);
logger.warn("Error occurred while fetching account: {0}", exception.getMessage());
logger.debug("Error occurred while fetching account", exception);
}

// hide all errors occurred while fetching account
return responseForUnknownAccount(accountId);
}

private Future<Account> responseForUnknownAccount(String accountId) {
return enforceValidAccount
? Future.failedFuture(new UnauthorizedAccountException(
"Unauthorized account id: " + accountId, accountId))
: Future.succeededFuture(Account.empty(accountId));
return Future.failedFuture(
new UnauthorizedAccountException("Unauthorized account id: " + accountId, accountId));
}

private Future<Account> ensureAccountActive(Account account) {
final String accountId = account.getId();

return account.getStatus() == AccountStatus.inactive
? Future.failedFuture(new UnauthorizedAccountException(
"Account %s is inactive".formatted(accountId), accountId))
: Future.succeededFuture(account);
private static String accountErrorMessage(String message, HttpRequestContext httpRequest) {
return "%s, Url: %s and Referer: %s".formatted(
message,
httpRequest.getAbsoluteUri(),
httpRequest.getHeaders().get(HttpUtil.REFERER_HEADER));
}

private ExtRequest enrichExtRequest(ExtRequest ext, Account account) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.prebid.server.settings.model.AccountPrivacySandboxCookieDeprecationConfig;
import org.prebid.server.util.HttpUtil;

import java.util.Objects;
import java.util.Optional;

public class CookieDeprecationService {
Expand All @@ -26,20 +25,12 @@ public class CookieDeprecationService {
private static final String DEVICE_EXT_COOKIE_DEPRECATION_FIELD_NAME = "cdep";
private static final long DEFAULT_MAX_AGE = 604800L;

private final Account defaultAccount;

public CookieDeprecationService(Account defaultAccount) {
this.defaultAccount = Objects.requireNonNull(defaultAccount);
}

public PartitionedCookie makeCookie(Account account, RoutingContext routingContext) {
final Account resolvedAccount = account.isEmpty() ? defaultAccount : account;

if (hasDeprecationCookieInRequest(routingContext) || isCookieDeprecationDisabled(resolvedAccount)) {
if (hasDeprecationCookieInRequest(routingContext) || isCookieDeprecationDisabled(account)) {
return null;
}

final Long maxAge = getCookieDeprecationConfig(resolvedAccount)
final Long maxAge = getCookieDeprecationConfig(account)
.map(AccountPrivacySandboxCookieDeprecationConfig::getTtlSec)
.orElse(DEFAULT_MAX_AGE);

Expand All @@ -61,13 +52,9 @@ public BidRequest updateBidRequestDevice(BidRequest bidRequest, AuctionContext a
.get(HttpUtil.SEC_COOKIE_DEPRECATION);

final Account account = auctionContext.getAccount();
final Account resolvedAccount = account.isEmpty() ? defaultAccount : account;
final Device device = bidRequest.getDevice();

if (secCookieDeprecation == null
|| containsCookieDeprecation(device)
|| isCookieDeprecationDisabled(resolvedAccount)) {

if (secCookieDeprecation == null || containsCookieDeprecation(device) || isCookieDeprecationDisabled(account)) {
return bidRequest;
}

Expand Down
7 changes: 2 additions & 5 deletions src/main/java/org/prebid/server/floors/PriceFloorFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,8 @@ private void periodicFetch(String accountId) {
}

private Future<Account> accountById(String accountId) {
return StringUtils.isBlank(accountId)
? Future.succeededFuture()
: applicationSettings
.getAccountById(accountId, timeoutFactory.create(ACCOUNT_FETCH_TIMEOUT_MS))
.recover(ignored -> Future.succeededFuture());
return applicationSettings.getAccountById(accountId, timeoutFactory.create(ACCOUNT_FETCH_TIMEOUT_MS))
.otherwiseEmpty();
}

@Value(staticConstructor = "of")
Expand Down
Loading
Loading