Skip to content

Commit

Permalink
Add module-execution config on the host level (#3594)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoxaAntoxic authored Dec 5, 2024
1 parent ae475a5 commit 9a1f926
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 26 deletions.
7 changes: 4 additions & 3 deletions docs/config-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,6 @@ contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Pos
For targeting available next options:
- `settings.targeting.truncate-attr-chars` - set the max length for names of targeting keywords (0 means no truncation).

For modules:
- `settings.modules.require-config-to-invoke` - when enabled it requires a runtime config to exist for a module.

## Host Cookie
- `host-cookie.optout-cookie.name` - set the cookie name for optout checking.
- `host-cookie.optout-cookie.value` - set the cookie value for optout checking.
Expand Down Expand Up @@ -443,6 +440,10 @@ If not defined in config all other Health Checkers would be disabled and endpoin
- `analytics.pubstack.buffers.count` - threshold in events count for buffer to send events
- `analytics.pubstack.buffers.report-ttl-ms` - max period between two reports.

## Modules
- `hooks.admin.module-execution` - a key-value map, where a key is a module name and a value is a boolean, that defines whether modules hooks should/should not be always executed; if the module is not specified it is executed by default when it's present in the execution plan
- `settings.modules.require-config-to-invoke` - when enabled it requires a runtime config to exist for a module.

## Debugging
- `debug.override-token` - special string token for overriding Prebid Server account and/or adapter debug information presence in the auction response.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public Future<GroupResult<PAYLOAD>> execute() {
Future<GroupResult<PAYLOAD>> groupFuture = Future.succeededFuture(initialGroupResult);

for (final HookId hookId : group.getHookSequence()) {
if (!modulesExecution.getOrDefault(hookId.getModuleCode(), true)) {
if (!modulesExecution.get(hookId.getModuleCode())) {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public class HookStageExecutor {

private final ExecutionPlan hostExecutionPlan;
private final ExecutionPlan defaultAccountExecutionPlan;
private final Map<String, Boolean> hostModuleExecution;
private final HookCatalog hookCatalog;
private final TimeoutFactory timeoutFactory;
private final Vertx vertx;
Expand All @@ -90,6 +91,7 @@ public class HookStageExecutor {

private HookStageExecutor(ExecutionPlan hostExecutionPlan,
ExecutionPlan defaultAccountExecutionPlan,
Map<String, Boolean> hostModuleExecution,
HookCatalog hookCatalog,
TimeoutFactory timeoutFactory,
Vertx vertx,
Expand All @@ -105,10 +107,12 @@ private HookStageExecutor(ExecutionPlan hostExecutionPlan,
this.clock = clock;
this.mapper = mapper;
this.isConfigToInvokeRequired = isConfigToInvokeRequired;
this.hostModuleExecution = hostModuleExecution;
}

public static HookStageExecutor create(String hostExecutionPlan,
String defaultAccountExecutionPlan,
Map<String, Boolean> hostModuleExecution,
HookCatalog hookCatalog,
TimeoutFactory timeoutFactory,
Vertx vertx,
Expand All @@ -122,6 +126,7 @@ public static HookStageExecutor create(String hostExecutionPlan,
return new HookStageExecutor(
parseAndValidateExecutionPlan(hostExecutionPlan, mapper, hookCatalog),
parseAndValidateExecutionPlan(defaultAccountExecutionPlan, mapper, hookCatalog),
hostModuleExecution,
hookCatalog,
Objects.requireNonNull(timeoutFactory),
Objects.requireNonNull(vertx),
Expand Down Expand Up @@ -185,7 +190,7 @@ public Future<HookStageExecutionResult<EntrypointPayload>> executeEntrypointStag
.withHookProvider(hookProviderForEntrypointStage(context))
.withInitialPayload(EntrypointPayloadImpl.of(queryParams, headers, body))
.withInvocationContextProvider(invocationContextProvider(endpoint))
.withModulesExecution(Collections.emptyMap())
.withModulesExecution(DefaultedMap.defaultedMap(hostModuleExecution, true))
.withRejectAllowed(true)
.execute();
}
Expand Down Expand Up @@ -374,6 +379,7 @@ private Map<String, Boolean> modulesExecutionForAccount(Account account) {
.forEach(module -> resultModulesExecution.computeIfAbsent(module, key -> true));
}

resultModulesExecution.putAll(hostModuleExecution);
return DefaultedMap.defaultedMap(resultModulesExecution, !isConfigToInvokeRequired);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.prebid.server.hooks.execution.HookStageExecutor;
import org.prebid.server.hooks.v1.Module;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.settings.model.HooksAdminConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand All @@ -16,6 +17,8 @@

import java.time.Clock;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

@Configuration
public class HooksConfiguration {
Expand All @@ -38,6 +41,9 @@ HookStageExecutor hookStageExecutor(HooksConfigurationProperties hooksConfigurat
return HookStageExecutor.create(
hooksConfiguration.getHostExecutionPlan(),
hooksConfiguration.getDefaultAccountExecutionPlan(),
Optional.ofNullable(hooksConfiguration.getAdmin())
.map(HooksAdminConfig::getModuleExecution)
.orElseGet(Collections::emptyMap),
hookCatalog,
timeoutFactory,
vertx,
Expand All @@ -60,5 +66,7 @@ private static class HooksConfigurationProperties {
String hostExecutionPlan;

String defaultAccountExecutionPlan;

HooksAdminConfig admin;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ class PrebidServerContainer extends GenericContainer<PrebidServerContainer> {

private static String normalizeProperty(String property) {
property.replace(".", "_")
.replace("-", "")
.replace("[", "_")
.replace("]", "_")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import org.prebid.server.functional.model.request.auction.TraceLevel
import org.prebid.server.functional.model.response.auction.InvocationResult
import org.prebid.server.functional.service.PrebidServerService
import org.prebid.server.functional.util.PBSUtils
import spock.lang.PendingFeature

import static org.prebid.server.functional.model.ModuleName.ORTB2_BLOCKING
import static org.prebid.server.functional.model.ModuleName.PB_RICHMEDIA_FILTER
Expand Down Expand Up @@ -293,7 +292,6 @@ class GeneralModuleSpec extends ModuleBaseSpec {
modulesConfig << [null, new PbsModulesConfig()]
}

@PendingFeature
def "PBS should call all modules without account config when modules enabled in module-execution host config"() {
given: "PBS service with module-execution config"
def pbsConfig = MULTI_MODULE_CONFIG + ENABLED_INVOKE_CONFIG +
Expand Down Expand Up @@ -333,6 +331,37 @@ class GeneralModuleSpec extends ModuleBaseSpec {
pbsServiceFactory.removeContainer(pbsConfig)
}

def "PBS shouldn't call any module without account config when modules disabled in module-execution host config"() {
given: "PBS service with module-execution config"
def pbsConfig = MULTI_MODULE_CONFIG + ENABLED_INVOKE_CONFIG +
[("hooks.admin.module-execution.${ORTB2_BLOCKING.code}".toString()): 'false']
def pbsServiceWithMultipleModules = pbsServiceFactory.getService(pbsConfig)

and: "Default bid request with verbose trace"
def bidRequest = defaultBidRequest.tap {
ext.prebid.trace = TraceLevel.VERBOSE
}

and: "Flush metrics"
flushMetrics(pbsServiceWithMultipleModules)

when: "PBS processes auction request"
def response = pbsServiceWithMultipleModules.sendAuctionRequest(bidRequest)

then: "PBS response shouldn't include trace information about no-called modules"
assert !response?.ext?.prebid?.modules?.trace?.stages?.outcomes?.groups?.invocationResults?.flatten()

and: "Ortb2blocking module call metrics shouldn't be updated"
def metrics = pbsServiceWithMultipleModuleWithRequireInvoke.sendCollectedMetricsRequest()
assert !metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)]
assert !metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)]
assert !metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)]
assert !metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)]

cleanup: "Stop and remove pbs container"
pbsServiceFactory.removeContainer(pbsConfig)
}

def "PBS should call module without account config when default-account module-execution config enabled module"() {
given: "PBS service with module-execution and default account configs"
def defaultAccountConfigSettings = AccountConfig.defaultAccountConfig.tap {
Expand All @@ -358,19 +387,15 @@ class GeneralModuleSpec extends ModuleBaseSpec {
when: "PBS processes auction request"
def response = pbsServiceWithMultipleModules.sendAuctionRequest(bidRequest)

then: "PBS response should include trace information about called modules"
verifyAll(response?.ext?.prebid?.modules?.trace?.stages?.outcomes?.groups?.invocationResults?.flatten() as List<InvocationResult>) {
it.status == [SUCCESS, SUCCESS]
it.action == [NO_ACTION, NO_ACTION]
it.hookId.moduleCode.sort() == [ORTB2_BLOCKING, ORTB2_BLOCKING].code.sort()
}
then: "PBS response shouldn't include trace information about no-called modules"
assert !response?.ext?.prebid?.modules?.trace?.stages?.outcomes?.groups?.invocationResults?.flatten()

and: "Ortb2blocking module call metrics should be updated"
def metrics = pbsServiceWithMultipleModules.sendCollectedMetricsRequest()
assert metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)] == 1
assert metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)] == 1
assert metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)] == 1
assert metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)] == 1
and: "Ortb2blocking module call metrics shouldn't be updated"
def metrics = pbsServiceWithMultipleModuleWithRequireInvoke.sendCollectedMetricsRequest()
assert !metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)]
assert !metrics[CALL_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)]
assert !metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, BIDDER_REQUEST.metricValue, ORTB2_BLOCKING_BIDDER_REQUEST.code)]
assert !metrics[NOOP_METRIC.formatted(ORTB2_BLOCKING.code, RAW_BIDDER_RESPONSE.metricValue, ORTB2_BLOCKING_RAW_BIDDER_RESPONSE.code)]

and: "RB-Richmedia-Filter module call metrics shouldn't be updated"
assert !metrics[CALL_METRIC.formatted(PB_RICHMEDIA_FILTER.code, ALL_PROCESSED_BID_RESPONSES.metricValue, PB_RICHMEDIA_FILTER_ALL_PROCESSED_RESPONSES.code)]
Expand Down
Loading

0 comments on commit 9a1f926

Please sign in to comment.